From f4d8115023913918811c156305ee953422aa5b72 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Mon, 18 Sep 2023 17:02:13 +0200 Subject: [PATCH 001/189] Initialize package readmes --- .../Microsoft.Bcl.Cryptography/src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ src/libraries/System.CodeDom/src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Composition.Hosting/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Composition.Runtime/src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Composition/src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Formats.Cbor/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.IO.Hashing/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.IO.Packaging/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.IO.Pipelines/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Memory.Data/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Numerics.Tensors/src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Reflection.Context/src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Text.Encodings.Web/src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../src/PACKAGE.md | 44 +++++++++++++++++++ .../System.Windows.Extensions/src/PACKAGE.md | 44 +++++++++++++++++++ 47 files changed, 2068 insertions(+) create mode 100644 src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md create mode 100644 src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md create mode 100644 src/libraries/System.CodeDom/src/PACKAGE.md create mode 100644 src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md create mode 100644 src/libraries/System.ComponentModel.Composition/src/PACKAGE.md create mode 100644 src/libraries/System.Composition.AttributedModel/src/PACKAGE.md create mode 100644 src/libraries/System.Composition.Convention/src/PACKAGE.md create mode 100644 src/libraries/System.Composition.Hosting/src/PACKAGE.md create mode 100644 src/libraries/System.Composition.Runtime/src/PACKAGE.md create mode 100644 src/libraries/System.Composition.TypedParts/src/PACKAGE.md create mode 100644 src/libraries/System.Composition/src/PACKAGE.md create mode 100644 src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md create mode 100644 src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md create mode 100644 src/libraries/System.Formats.Cbor/src/PACKAGE.md create mode 100644 src/libraries/System.IO.Hashing/src/PACKAGE.md create mode 100644 src/libraries/System.IO.Packaging/src/PACKAGE.md create mode 100644 src/libraries/System.IO.Pipelines/src/PACKAGE.md create mode 100644 src/libraries/System.Memory.Data/src/PACKAGE.md create mode 100644 src/libraries/System.Numerics.Tensors/src/PACKAGE.md create mode 100644 src/libraries/System.Reflection.Context/src/PACKAGE.md create mode 100644 src/libraries/System.Resources.Extensions/src/PACKAGE.md create mode 100644 src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md create mode 100644 src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md create mode 100644 src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md create mode 100644 src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md create mode 100644 src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md create mode 100644 src/libraries/System.Security.Permissions/src/PACKAGE.md create mode 100644 src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md create mode 100644 src/libraries/System.Text.Encodings.Web/src/PACKAGE.md create mode 100644 src/libraries/System.Threading.AccessControl/src/PACKAGE.md create mode 100644 src/libraries/System.Threading.RateLimiting/src/PACKAGE.md create mode 100644 src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md create mode 100644 src/libraries/System.Windows.Extensions/src/PACKAGE.md diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md new file mode 100644 index 00000000000000..df639a6cc16479 --- /dev/null +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Bcl.Cryptography is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md new file mode 100644 index 00000000000000..06c9f1bee029a6 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.Caching.Abstractions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md new file mode 100644 index 00000000000000..74ad997aae4234 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.DependencyInjection.Specification.Tests is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md new file mode 100644 index 00000000000000..cf96b327b1cf99 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.FileProviders.Abstractions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md new file mode 100644 index 00000000000000..f0d2c3ffecb22b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.FileProviders.Composite is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md new file mode 100644 index 00000000000000..2857ab5e67878b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.FileProviders.Physical is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md new file mode 100644 index 00000000000000..9e3801f758a082 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.FileSystemGlobbing is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md new file mode 100644 index 00000000000000..0f9358494ba743 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.Hosting.Systemd is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md new file mode 100644 index 00000000000000..ecfe712b7012c3 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.Logging.Configuration is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md new file mode 100644 index 00000000000000..3e3ea25e38e979 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.Logging.EventSource is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md new file mode 100644 index 00000000000000..44df455dbcd1c4 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.Logging.TraceSource is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md new file mode 100644 index 00000000000000..66b8bc76efc55b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Extensions.Options.DataAnnotations is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md new file mode 100644 index 00000000000000..f392d7f5c1da8a --- /dev/null +++ b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.NET.WebAssembly.Threading is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md new file mode 100644 index 00000000000000..ada4ea27b65301 --- /dev/null +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Win32.Registry.AccessControl is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md new file mode 100644 index 00000000000000..20049fb3788f8c --- /dev/null +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.Win32.SystemEvents is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md b/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md new file mode 100644 index 00000000000000..3881bbb005ffa1 --- /dev/null +++ b/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +Microsoft.XmlSerializer.Generator is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.CodeDom/src/PACKAGE.md b/src/libraries/System.CodeDom/src/PACKAGE.md new file mode 100644 index 00000000000000..8d0fbc58725746 --- /dev/null +++ b/src/libraries/System.CodeDom/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.CodeDom is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md b/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md new file mode 100644 index 00000000000000..eba3a44e0e1783 --- /dev/null +++ b/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.ComponentModel.Composition.Registration is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md b/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md new file mode 100644 index 00000000000000..f91a8363665d1b --- /dev/null +++ b/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.ComponentModel.Composition is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md b/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md new file mode 100644 index 00000000000000..fa20133ba6a869 --- /dev/null +++ b/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Composition.AttributedModel is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Composition.Convention/src/PACKAGE.md b/src/libraries/System.Composition.Convention/src/PACKAGE.md new file mode 100644 index 00000000000000..a6486dee287c10 --- /dev/null +++ b/src/libraries/System.Composition.Convention/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Composition.Convention is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Composition.Hosting/src/PACKAGE.md b/src/libraries/System.Composition.Hosting/src/PACKAGE.md new file mode 100644 index 00000000000000..65273363edc92e --- /dev/null +++ b/src/libraries/System.Composition.Hosting/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Composition.Hosting is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Composition.Runtime/src/PACKAGE.md b/src/libraries/System.Composition.Runtime/src/PACKAGE.md new file mode 100644 index 00000000000000..d0b11f40ae387b --- /dev/null +++ b/src/libraries/System.Composition.Runtime/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Composition.Runtime is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Composition.TypedParts/src/PACKAGE.md b/src/libraries/System.Composition.TypedParts/src/PACKAGE.md new file mode 100644 index 00000000000000..8f1234fe5eb7ef --- /dev/null +++ b/src/libraries/System.Composition.TypedParts/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Composition.TypedParts is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Composition/src/PACKAGE.md b/src/libraries/System.Composition/src/PACKAGE.md new file mode 100644 index 00000000000000..aed1bb67a26524 --- /dev/null +++ b/src/libraries/System.Composition/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Composition is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md b/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md new file mode 100644 index 00000000000000..dd721a9688da59 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Diagnostics.DiagnosticSource is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md b/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md new file mode 100644 index 00000000000000..c06e75e48ae1ee --- /dev/null +++ b/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.DirectoryServices.Protocols is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Formats.Cbor/src/PACKAGE.md b/src/libraries/System.Formats.Cbor/src/PACKAGE.md new file mode 100644 index 00000000000000..96474955ed1b16 --- /dev/null +++ b/src/libraries/System.Formats.Cbor/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Formats.Cbor is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.IO.Hashing/src/PACKAGE.md b/src/libraries/System.IO.Hashing/src/PACKAGE.md new file mode 100644 index 00000000000000..9e6719c28fd4a8 --- /dev/null +++ b/src/libraries/System.IO.Hashing/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.IO.Hashing is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.IO.Packaging/src/PACKAGE.md b/src/libraries/System.IO.Packaging/src/PACKAGE.md new file mode 100644 index 00000000000000..f3cbe0ec774123 --- /dev/null +++ b/src/libraries/System.IO.Packaging/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.IO.Packaging is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.IO.Pipelines/src/PACKAGE.md b/src/libraries/System.IO.Pipelines/src/PACKAGE.md new file mode 100644 index 00000000000000..07b76a5a6e0bbf --- /dev/null +++ b/src/libraries/System.IO.Pipelines/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.IO.Pipelines is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Memory.Data/src/PACKAGE.md b/src/libraries/System.Memory.Data/src/PACKAGE.md new file mode 100644 index 00000000000000..a41bbe80d1089f --- /dev/null +++ b/src/libraries/System.Memory.Data/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Memory.Data is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md new file mode 100644 index 00000000000000..3959650cfacd5a --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Numerics.Tensors is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Reflection.Context/src/PACKAGE.md b/src/libraries/System.Reflection.Context/src/PACKAGE.md new file mode 100644 index 00000000000000..6b3ea0dccc6e99 --- /dev/null +++ b/src/libraries/System.Reflection.Context/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Reflection.Context is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Resources.Extensions/src/PACKAGE.md b/src/libraries/System.Resources.Extensions/src/PACKAGE.md new file mode 100644 index 00000000000000..bc740503b6c4f9 --- /dev/null +++ b/src/libraries/System.Resources.Extensions/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Resources.Extensions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md b/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md new file mode 100644 index 00000000000000..32703b7a9171eb --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Runtime.Serialization.Schema is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md new file mode 100644 index 00000000000000..c07b43e1d89789 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Security.Cryptography.Cose is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md new file mode 100644 index 00000000000000..c6b169ef2a5f9f --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Security.Cryptography.Pkcs is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md new file mode 100644 index 00000000000000..3540bcfc25384b --- /dev/null +++ b/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Security.Cryptography.ProtectedData is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md new file mode 100644 index 00000000000000..66dd9122df501e --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Security.Cryptography.Xml is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Security.Permissions/src/PACKAGE.md b/src/libraries/System.Security.Permissions/src/PACKAGE.md new file mode 100644 index 00000000000000..42702c77c66c44 --- /dev/null +++ b/src/libraries/System.Security.Permissions/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Security.Permissions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md new file mode 100644 index 00000000000000..1fc03ca6f90070 --- /dev/null +++ b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.ServiceModel.Syndication is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md new file mode 100644 index 00000000000000..b052cd5f92a26d --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Text.Encodings.Web is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md new file mode 100644 index 00000000000000..ac3658ca62f325 --- /dev/null +++ b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Threading.AccessControl is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md new file mode 100644 index 00000000000000..72a0f2a9bc8d97 --- /dev/null +++ b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Threading.RateLimiting is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md b/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md new file mode 100644 index 00000000000000..d435316b7785b3 --- /dev/null +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Threading.Tasks.Dataflow is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file diff --git a/src/libraries/System.Windows.Extensions/src/PACKAGE.md b/src/libraries/System.Windows.Extensions/src/PACKAGE.md new file mode 100644 index 00000000000000..90d18f7b5643e5 --- /dev/null +++ b/src/libraries/System.Windows.Extensions/src/PACKAGE.md @@ -0,0 +1,44 @@ +## About + + + + + +## Key Features + + + +* +* +* + +## How to Use + + + +## Main Types + + + +The main types provided by this library are: + +* `` +* `` +* `` + +## Addtional Documentation + + + +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) + +## Related Packages + + + +## Feedback & Contributing + + + +System.Windows.Extensions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file From b608c28fe8c1237ea013719b540093fb6770b3eb Mon Sep 17 00:00:00 2001 From: Illia Larka Date: Tue, 3 Oct 2023 23:01:12 +0200 Subject: [PATCH 002/189] Microsoft.Extensions.Logging.TraceSource package readme update (#92962) Fix #92228 --- .../src/PACKAGE.md | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md index 44df455dbcd1c4..db6f1de26e6c44 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md @@ -2,41 +2,72 @@ - +Implements a trace logger provider for the .NET logging infrastructre facilitating enhanced logging capabilities and trace-level diagnostics in application by writing messages to a trace listener using System.Diagnostic.TraceSource. ## Key Features -* -* -* +* Seamless integration with .NET logging infrastructure. +* Fine-grained control over trace messages using SourceSwitch. +* A set of builder methods to configure logging infrastructure. ## How to Use +The Microsoft.Extensions.Logging.TraceSource library provides extension methods to the logger factory and the logger builder to add a trace source with trace listeners. + +```csharp +using System.Diagnostics; +using Microsoft.Extensions.Logging; + +using var consoleTraceListener = new ConsoleTraceListener(); +using var textWriterTraceListener = new TextWriterTraceListener("/traces.txt"); +using var loggerFactory = LoggerFactory.Create(builder => +{ + builder + .AddTraceSource(new SourceSwitch("Something") { Level = SourceLevels.All }, consoleTraceListener) + .AddTraceSource(new SourceSwitch("HouseKeeping") { Level = SourceLevels.All }, textWriterTraceListener); +}); + +var logger = loggerFactory.CreateLogger(); + +logger.LogInformation("Information message."); +// Program Information: 0 : Information message. +logger.LogWarning("Warning message."); +// Program Warning: 0 : Warning message. + +var traceSource = new TraceSource("HouseKeeping", SourceLevels.All); +traceSource.Listeners.Add(consoleTraceListener); +traceSource.Listeners.Add(textWriterTraceListener); + +traceSource.TraceEvent(TraceEventType.Error, 0, "Error message."); +//HouseKeeping Error: 0 : Error message. +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider` ## Addtional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.tracesource) ## Related Packages +* Abstractions for dependency injection: [Microsoft.Extensions.DependencyInjection.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/) +* Defult implementation of logging infrastructure: [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging/) +* Abstractions for logging: [Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions/) + ## Feedback & Contributing From 5ab4d08f05e560420431ca8a431b426d1a2f8afa Mon Sep 17 00:00:00 2001 From: Illia Larka Date: Wed, 4 Oct 2023 11:46:03 +0200 Subject: [PATCH 003/189] Fix PACKAGE.md typo (#92989) Typo in word 'Defult' fixed to 'Default'. Fix #92228 --- .../Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md index db6f1de26e6c44..f19aa7adb5f9ff 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md @@ -65,7 +65,7 @@ The main types provided by this library are: * Abstractions for dependency injection: [Microsoft.Extensions.DependencyInjection.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions/) -* Defult implementation of logging infrastructure: [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging/) +* Default implementation of logging infrastructure: [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging/) * Abstractions for logging: [Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions/) ## Feedback & Contributing From 8a165a5941f2aaf57ae660e40507a79a349d07a7 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 4 Oct 2023 12:26:19 -0400 Subject: [PATCH 004/189] Update Microsoft.Bcl.Cryptography/src/PACKAGE.md --- .../Microsoft.Bcl.Cryptography/src/PACKAGE.md | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md index df639a6cc16479..8b19311f0a7cd8 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md @@ -1,44 +1,39 @@ ## About - - - +This library provides some cryptographic types and functionality for .NET Standard and .NET Framework. This library is not necessary nor recommended when targeting versions of .NET that include the relevant support. ## Key Features - - -* -* -* +* Enables the use of some cryptographic functionality on older .NET platforms. ## How to Use - +This package should only be used by platforms where the desired functionality is not built-in. -## Main Types +```C# +using System.Security.Cryptography; + +internal static class Program +{ + private static void Main() + { + byte[] key = LoadKey(); + SP800108HmacCounterKdf kbkdf = new(key, HashAlgorithmName.SHA256); + byte[] derivedKey = kbkdf.DeriveKey("label"u8, "context"u8, derivedKeyLengthInBytes: 32); + } +} +``` - +## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.Security.Cryptography.SP800108HmacCounterKdf` ## Addtional Documentation - - -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/System.Security.Cryptography) ## Feedback & Contributing - - -Microsoft.Bcl.Cryptography is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Bcl.Cryptography is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 37120cdcd90b79d5dbdf168354d9d4db27058fab Mon Sep 17 00:00:00 2001 From: Illia Larka Date: Mon, 9 Oct 2023 10:56:12 +0200 Subject: [PATCH 005/189] System.Formats.Cbor package's readme (#93021) * Remove conceptual docummentation item Removed since is not applicable * List types library provides Add list of types which library provides. Listed public types, means exposed types. * Add description and usage examples Add library description. Add few code examples showing basic functionality for CBOR wirter and reader types. * Add related packages section Since different targets have different dependencies so I have divided for each section. * Improve short description Add 'format' word after abbreviation to have an object in sentence. * Remove related packages section Since there are no related packages. * Improve array reading/writing example --- .../System.Formats.Cbor/src/PACKAGE.md | 77 +++++++++++++++---- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Formats.Cbor/src/PACKAGE.md b/src/libraries/System.Formats.Cbor/src/PACKAGE.md index 96474955ed1b16..ff26f0debbdc0a 100644 --- a/src/libraries/System.Formats.Cbor/src/PACKAGE.md +++ b/src/libraries/System.Formats.Cbor/src/PACKAGE.md @@ -2,40 +2,91 @@ +Provides support for reading and writing values in Concise Binary Object Representation (CBOR) format, as originally defined in [IETF RFC 7049](https://www.ietf.org/rfc/rfc7049.html). ## Key Features -* -* -* +* Reader and writer types for the CBOR format. +* Built-in support for different CBOR conformance modes. ## How to Use +Write and read primitives: + +```csharp +using System.Formats.Cbor; + +var cborWriter = new CborWriter(CborConformanceMode.Lax); +cborWriter.WriteTextString("Hello World"); + +var cborReader = new CborReader(cborWriter.Encode(), CborConformanceMode.Lax); +Console.WriteLine(cborReader.ReadTextString()); +// Hello World +``` + +Write and read an array: + +```csharp +var cborWriter = new CborWriter(CborConformanceMode.Lax); +cborWriter.WriteStartArray(5); +for (var index = 0; index < 5; index++) +{ + cborWriter.WriteInt32(index); +} +cborWriter.WriteEndArray(); + +var cborReader = new CborReader(cborWriter.Encode(), CborConformanceMode.Lax); +var arrayLength = cborReader.ReadStartArray(); +for (var index = 0; index < arrayLength; index++) +{ + Console.Write(cborReader.ReadInt32()); +} +// 01234 +cborReader.ReadEndArray(); +``` + +Inspect writer and reader state: + +```csharp +var cborWriter = new CborWriter(CborConformanceMode.Lax); +cborWriter.WriteTextString("SomeArray"); +Console.WriteLine(cborWriter.BytesWritten); +// 10 +Console.WriteLine(cborWriter.IsWriteCompleted); +// True + +var cborReader = new CborReader(cborWriter.Encode(), CborConformanceMode.Lax); +Console.WriteLine(cborReader.BytesRemaining); +// 10 +Console.WriteLine(cborReader.ReadTextString()); +// SomeArray +Console.WriteLine(cborReader.BytesRemaining); +// 0 +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.Formats.Cbor.CborReader` +* `System.Formats.Cbor.CborWriter` +* `System.Formats.Cbor.CborReaderState` +* `System.Formats.Cbor.CborConformanceMode` +* `System.Formats.Cbor.CborContentException` +* `System.Formats.Cbor.CborTag` -## Addtional Documentation +## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/system.formats.cbor) ## Feedback & Contributing From 80426d89847d43fa0a7a39730af3002b008b9628 Mon Sep 17 00:00:00 2001 From: Logan Payton <18620874+EnsignPayton@users.noreply.github.com> Date: Mon, 9 Oct 2023 03:59:47 -0500 Subject: [PATCH 006/189] Microsoft.Extensions.FileSystemGlobbing PACKAGE.md (#93123) * Microsoft.Extensions.FileSystemGlobbing PACKAGE.md Fill out details for `Microsoft.Extensions.FileSystemGlobbing` NuGet package readme See https://github.com/dotnet/runtime/pull/92228 * Update src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md * Update src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md --------- Co-authored-by: Viktor Hofer --- .../src/PACKAGE.md | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md index 9e3801f758a082..433fa943e1172a 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md @@ -2,40 +2,48 @@ - +Provides support for matching file system names/paths using [glob patterns](https://en.wikipedia.org/wiki/Glob_(programming)). ## Key Features -* -* -* +* Contains the `Matcher` type, which can be used to match files in the file system based on user-defined patterns. ## How to Use +Get all matching files: + +```c# +using Microsoft.Extensions.FileSystemGlobbing; + +Matcher matcher = new(); +matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" }); + +string searchDirectory = "../starting-folder/"; + +IEnumerable matchingFiles = matcher.GetResultsInFullPath(searchDirectory); + +// Use matchingFiles if there are any found. +// The files in this collection are fully qualified file system paths. +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.FileSystemGlobbing.Matcher` -## Addtional Documentation +## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/core/extensions/file-globbing) +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.filesystemglobbing) ## Feedback & Contributing From 0db2545b283870fd1337f12084e78fe5a658a7a4 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Mon, 9 Oct 2023 09:04:24 +0000 Subject: [PATCH 007/189] Fix typo in package readmes --- src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md | 2 +- .../src/PACKAGE.md | 2 +- .../src/PACKAGE.md | 2 +- .../Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md | 2 +- .../Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md | 2 +- .../Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md | 2 +- src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md | 2 +- src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md | 2 +- src/libraries/System.CodeDom/src/PACKAGE.md | 2 +- .../src/PACKAGE.md | 2 +- src/libraries/System.ComponentModel.Composition/src/PACKAGE.md | 2 +- src/libraries/System.Composition.AttributedModel/src/PACKAGE.md | 2 +- src/libraries/System.Composition.Convention/src/PACKAGE.md | 2 +- src/libraries/System.Composition.Hosting/src/PACKAGE.md | 2 +- src/libraries/System.Composition.Runtime/src/PACKAGE.md | 2 +- src/libraries/System.Composition.TypedParts/src/PACKAGE.md | 2 +- src/libraries/System.Composition/src/PACKAGE.md | 2 +- .../System.Diagnostics.DiagnosticSource/src/PACKAGE.md | 2 +- src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md | 2 +- src/libraries/System.IO.Hashing/src/PACKAGE.md | 2 +- src/libraries/System.IO.Packaging/src/PACKAGE.md | 2 +- src/libraries/System.IO.Pipelines/src/PACKAGE.md | 2 +- src/libraries/System.Memory.Data/src/PACKAGE.md | 2 +- src/libraries/System.Numerics.Tensors/src/PACKAGE.md | 2 +- src/libraries/System.Reflection.Context/src/PACKAGE.md | 2 +- src/libraries/System.Resources.Extensions/src/PACKAGE.md | 2 +- .../System.Runtime.Serialization.Schema/src/PACKAGE.md | 2 +- src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md | 2 +- src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md | 2 +- .../System.Security.Cryptography.ProtectedData/src/PACKAGE.md | 2 +- src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md | 2 +- src/libraries/System.Security.Permissions/src/PACKAGE.md | 2 +- src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md | 2 +- src/libraries/System.Text.Encodings.Web/src/PACKAGE.md | 2 +- src/libraries/System.Threading.AccessControl/src/PACKAGE.md | 2 +- src/libraries/System.Threading.RateLimiting/src/PACKAGE.md | 2 +- src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md | 2 +- src/libraries/System.Windows.Extensions/src/PACKAGE.md | 2 +- 45 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md index 8b19311f0a7cd8..4346252b24386b 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md @@ -30,7 +30,7 @@ The main types provided by this library are: * `System.Security.Cryptography.SP800108HmacCounterKdf` -## Addtional Documentation +## Additional Documentation * [API documentation](https://learn.microsoft.com/en-us/dotnet/api/System.Security.Cryptography) diff --git a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md index 06c9f1bee029a6..651e3c04c98405 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md index 74ad997aae4234..79b5aea01302e5 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md index cf96b327b1cf99..efb7ca7c9133cc 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md index f0d2c3ffecb22b..122fbecfa2ba91 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md index 2857ab5e67878b..bf3378a56e63dd 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md index 0f9358494ba743..ffb5501c8568fe 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md index ecfe712b7012c3..7c38fc44d350f7 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md index 3e3ea25e38e979..8a67a2888f46db 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md index f19aa7adb5f9ff..25abc578ba6d36 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md @@ -54,7 +54,7 @@ The main types provided by this library are: * `Microsoft.Extensions.Logging.TraceSource.TraceSourceLoggerProvider` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md index 66b8bc76efc55b..3bf3280ad4cf1b 100644 --- a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md index f392d7f5c1da8a..009758b7e5f78e 100644 --- a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md +++ b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md index ada4ea27b65301..f74e2de2b1ea32 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md index 20049fb3788f8c..4d4256fedf7446 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md b/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md index 3881bbb005ffa1..b83986018d2e70 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md +++ b/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.CodeDom/src/PACKAGE.md b/src/libraries/System.CodeDom/src/PACKAGE.md index 8d0fbc58725746..259e0feb50cece 100644 --- a/src/libraries/System.CodeDom/src/PACKAGE.md +++ b/src/libraries/System.CodeDom/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md b/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md index eba3a44e0e1783..2fa0eb6196f3c2 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md +++ b/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md b/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md index f91a8363665d1b..0096b2acdca3fa 100644 --- a/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md +++ b/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md b/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md index fa20133ba6a869..1f533435c4b860 100644 --- a/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md +++ b/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Composition.Convention/src/PACKAGE.md b/src/libraries/System.Composition.Convention/src/PACKAGE.md index a6486dee287c10..8cad9d889483b0 100644 --- a/src/libraries/System.Composition.Convention/src/PACKAGE.md +++ b/src/libraries/System.Composition.Convention/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Composition.Hosting/src/PACKAGE.md b/src/libraries/System.Composition.Hosting/src/PACKAGE.md index 65273363edc92e..be4b5f096e0e07 100644 --- a/src/libraries/System.Composition.Hosting/src/PACKAGE.md +++ b/src/libraries/System.Composition.Hosting/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Composition.Runtime/src/PACKAGE.md b/src/libraries/System.Composition.Runtime/src/PACKAGE.md index d0b11f40ae387b..3e30bc15c68321 100644 --- a/src/libraries/System.Composition.Runtime/src/PACKAGE.md +++ b/src/libraries/System.Composition.Runtime/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Composition.TypedParts/src/PACKAGE.md b/src/libraries/System.Composition.TypedParts/src/PACKAGE.md index 8f1234fe5eb7ef..cf5a4d1cb02ed9 100644 --- a/src/libraries/System.Composition.TypedParts/src/PACKAGE.md +++ b/src/libraries/System.Composition.TypedParts/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Composition/src/PACKAGE.md b/src/libraries/System.Composition/src/PACKAGE.md index aed1bb67a26524..33a8905040b5f0 100644 --- a/src/libraries/System.Composition/src/PACKAGE.md +++ b/src/libraries/System.Composition/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md b/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md index dd721a9688da59..69b5def89a51cb 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md b/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md index c06e75e48ae1ee..61bbd897786b2c 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md +++ b/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.IO.Hashing/src/PACKAGE.md b/src/libraries/System.IO.Hashing/src/PACKAGE.md index 9e6719c28fd4a8..5bb2b571759074 100644 --- a/src/libraries/System.IO.Hashing/src/PACKAGE.md +++ b/src/libraries/System.IO.Hashing/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.IO.Packaging/src/PACKAGE.md b/src/libraries/System.IO.Packaging/src/PACKAGE.md index f3cbe0ec774123..8ada821aaf440b 100644 --- a/src/libraries/System.IO.Packaging/src/PACKAGE.md +++ b/src/libraries/System.IO.Packaging/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.IO.Pipelines/src/PACKAGE.md b/src/libraries/System.IO.Pipelines/src/PACKAGE.md index 07b76a5a6e0bbf..97b03f5e636c6a 100644 --- a/src/libraries/System.IO.Pipelines/src/PACKAGE.md +++ b/src/libraries/System.IO.Pipelines/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Memory.Data/src/PACKAGE.md b/src/libraries/System.Memory.Data/src/PACKAGE.md index a41bbe80d1089f..9c4f30806907e1 100644 --- a/src/libraries/System.Memory.Data/src/PACKAGE.md +++ b/src/libraries/System.Memory.Data/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md index 3959650cfacd5a..22d1b422ffccf0 100644 --- a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md +++ b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Reflection.Context/src/PACKAGE.md b/src/libraries/System.Reflection.Context/src/PACKAGE.md index 6b3ea0dccc6e99..74ea38d8033bca 100644 --- a/src/libraries/System.Reflection.Context/src/PACKAGE.md +++ b/src/libraries/System.Reflection.Context/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Resources.Extensions/src/PACKAGE.md b/src/libraries/System.Resources.Extensions/src/PACKAGE.md index bc740503b6c4f9..714d6c3fa8d4b6 100644 --- a/src/libraries/System.Resources.Extensions/src/PACKAGE.md +++ b/src/libraries/System.Resources.Extensions/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md b/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md index 32703b7a9171eb..cbc39f05fa9464 100644 --- a/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md +++ b/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md index c07b43e1d89789..f84f8af4d21079 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md index c6b169ef2a5f9f..77e903580c6789 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md index 3540bcfc25384b..09e76336690163 100644 --- a/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md index 66dd9122df501e..9822f0f06533af 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Security.Permissions/src/PACKAGE.md b/src/libraries/System.Security.Permissions/src/PACKAGE.md index 42702c77c66c44..ff4038b86dec7b 100644 --- a/src/libraries/System.Security.Permissions/src/PACKAGE.md +++ b/src/libraries/System.Security.Permissions/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md index 1fc03ca6f90070..5d99b5df6e9423 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md +++ b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md index b052cd5f92a26d..2c1dbff0851d5c 100644 --- a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md +++ b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md index ac3658ca62f325..eae4bdfe0f4ca8 100644 --- a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md +++ b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md index 72a0f2a9bc8d97..c1078de92361b0 100644 --- a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md +++ b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md b/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md index d435316b7785b3..c321c68a298166 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation diff --git a/src/libraries/System.Windows.Extensions/src/PACKAGE.md b/src/libraries/System.Windows.Extensions/src/PACKAGE.md index 90d18f7b5643e5..912da7dffa9a69 100644 --- a/src/libraries/System.Windows.Extensions/src/PACKAGE.md +++ b/src/libraries/System.Windows.Extensions/src/PACKAGE.md @@ -26,7 +26,7 @@ The main types provided by this library are: * `` * `` -## Addtional Documentation +## Additional Documentation From 3b1d2dc37bc4413091c933959c032b360d6c0ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 18 Oct 2023 00:34:17 +0200 Subject: [PATCH 008/189] Provide Microsoft.Extentions.FileProviders.Abstractions package readme (#93489) * Provide FileProviders.Abstractions package readme * Improve how to use section --- .../src/PACKAGE.md | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md index efb7ca7c9133cc..bbb9b68a06e4a6 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/PACKAGE.md @@ -2,43 +2,50 @@ - +Serves as the foundation for creating file providers in .NET, offering core abstractions to develop custom file providers capable of fetching files from various sources. ## Key Features -* -* -* +* Core abstractions for creating and managing file providers. +* Flexibility to develop custom file providers for fetching files from distinct sources. ## How to Use +This package is typically used with an implementation of the file provider abstractions, such as `Microsoft.Extensions.FileProviders.Composite` or `Microsoft.Extensions.FileProviders.Physical`. + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.FileProviders.IFileProvider` +* `Microsoft.Extensions.FileProviders.IDirectoryContents` +* `Microsoft.Extensions.FileProviders.IFileInfo` +* `Microsoft.Extensions.FileProviders.NullFileProvider` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/aspnet/core/fundamentals/file-providers) +* [Detect changes with change tokens](https://learn.microsoft.com/aspnet/core/fundamentals/change-tokens) +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.fileproviders) ## Related Packages +* File provider for physical files: [Microsoft.Extensions.FileProviders.Physical](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Physical/) +* File provider for files in embedded resources: [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded/) +* Composite file and directory providers: [Microsoft.Extensions.FileProviders.Composite](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Composite/) + ## Feedback & Contributing -Microsoft.Extensions.FileProviders.Abstractions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Extensions.FileProviders.Abstractions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 9d0b2061256a7bff7b90734c25c65137706ec1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 18 Oct 2023 00:34:36 +0200 Subject: [PATCH 009/189] Provide Microsoft.Extentions.FileProviders.Physical package readme (#93485) * Provide FileProviders.Physical package readme * Remove whitespace --- .../src/PACKAGE.md | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md index bf3378a56e63dd..6ffcd733120209 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PACKAGE.md @@ -2,43 +2,69 @@ +Provides an implementation of a physical file provider, facilitating file access and monitoring on the disk. The primary type, [`PhysicalFileProvider`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.fileproviders.physicalfileprovider), enables the lookup of files on disk and can watch for changes either via `FileSystemWatcher` or polling mechanisms. ## Key Features -* -* -* +* Easy access and monitoring of files on the disk. +* Ability to watch for file changes either by using `FileSystemWatcher` or through polling. ## How to Use +This library can be used to look up files on disk and monitor file changes effectively. +Below is an example of how to use the `PhysicalFileProvider` to access files on disk and monitor changes: + +```c# +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.FileProviders.Physical; + +using var provider = new PhysicalFileProvider(AppContext.BaseDirectory); + +Environment.SetEnvironmentVariable("DOTNET_USE_POLLING_FILE_WATCHER", "1"); + +var contents = provider.GetDirectoryContents(string.Empty); +foreach (PhysicalFileInfo fileInfo in contents) +{ + Console.WriteLine(fileInfo.PhysicalPath); +} + +var changeToken = provider.Watch("*.txt"); +changeToken.RegisterChangeCallback(_ => Console.WriteLine("Text file changed"), null); + +Console.ReadLine(); +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.FileProviders.PhysicalFileProvider` +* `Microsoft.Extensions.FileProviders.PhysicalDirectoryInfo` +* `Microsoft.Extensions.FileProviders.PhysicalFileInfo` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/aspnet/core/fundamentals/file-providers#physical-file-provider) +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.fileproviders.physical) ## Related Packages +* Abstractions of files and directories: [Microsoft.Extensions.FileProviders.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Abstractions/) +* File system globbing to find files matching a specified pattern: [Microsoft.Extensions.FileSystemGlobbing](https://www.nuget.org/packages/Microsoft.Extensions.FileSystemGlobbing/) + ## Feedback & Contributing -Microsoft.Extensions.FileProviders.Physical is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Extensions.FileProviders.Physical is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From fc488029eeeab8117dcb5a33fe60c3f00c3daaa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 18 Oct 2023 01:45:52 +0200 Subject: [PATCH 010/189] Provide DirectoryServices.Protocols package readme (#93518) --- .../src/PACKAGE.md | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md b/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md index 61bbd897786b2c..2938eee70fa8b0 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md +++ b/src/libraries/System.DirectoryServices.Protocols/src/PACKAGE.md @@ -2,43 +2,68 @@ +System.DirectoryServices.Protocols provides a managed implementation of Lightweight Directory Access Protocol (LDAP) version 3 and Directory Services Markup Language (DSML) version 2.0 (V2) standards. +It primarily uses the `LdapConnection` type for interacting with LDAP servers, using system native libraries to establish TCP/IP or UDP LDAP connections. +Supports both Windows and Unix, but certain features, such as setting client or server certificate options, are not available on Unix. ## Key Features -* -* -* +* Managed implementation of LDAP v3 and DSML V2 standards. ## How to Use +Using the `LdapConnection` type, you can establish connections to LDAP servers and issue requests. + +Here is a simple example: + +```csharp +using System.DirectoryServices.Protocols; + +// Create a new LdapConnection instance using the server URL. +using (LdapConnection connection = new LdapConnection("ldap.example.com")) { + + // Some credentials + connection.Credential = new NetworkCredential(dn, password); + + // Connect to the server + connection.Bind(); + + // Perform LDAP operations +} +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.DirectoryServices.Protocols.LdapConnection` +* `System.DirectoryServices.Protocols.DirectoryAttribute` +* `System.DirectoryServices.Protocols.DirectoryOperation` +* `System.DirectoryServices.Protocols.DirectoryRequest` +* `System.DirectoryServices.Protocols.DirectoryResponse` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.directoryservices.protocols) +* [Active Directory Domain Services](https://learn.microsoft.com/windows/win32/ad/active-directory-domain-services) ## Related Packages +* [System.DirectoryServices](https://www.nuget.org/packages/System.DirectoryServices/) + ## Feedback & Contributing -System.DirectoryServices.Protocols is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.DirectoryServices.Protocols is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 2610c534625ab78c271add500473e6cec03e0074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 18 Oct 2023 08:14:30 +0200 Subject: [PATCH 011/189] Provide System.IO.Hashing package readme (#93567) --- .../System.IO.Hashing/src/PACKAGE.md | 65 ++++++++++++++++--- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.IO.Hashing/src/PACKAGE.md b/src/libraries/System.IO.Hashing/src/PACKAGE.md index 5bb2b571759074..64fa5ff754845b 100644 --- a/src/libraries/System.IO.Hashing/src/PACKAGE.md +++ b/src/libraries/System.IO.Hashing/src/PACKAGE.md @@ -2,43 +2,90 @@ +System.IO.Hashing offers a variety of hash code algorithms. +Hash code algorithms are pivotal for generating unique values for objects based on their content, facilitating object comparisons, and detecting content alterations. +The namespace encompasses algorithms like CRC-32, CRC-64, xxHash3, xxHash32, xxHash64, and xxHash128, all engineered for swift and efficient hash code generation, with xxHash being an "Extremely fast hash algorithm". + +**Warning**: The hash functions provided by System.IO.Hashing are not suitable for security purposes such as handling passwords or verifying untrusted content. +For such security-critical applications, consider using cryptographic hash functions provided by the [System.Security.Cryptography](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography) namespace. ## Key Features -* -* -* +* Variety of hash code algorithms including CRC-32, CRC-64, xxHash3, xxHash32, xxHash64, and xxHash128. +* Implementations of CRC-32 and CRC-64 algorithms, as used in IEEE 802.3, and described in ECMA-182, Annex B respectively. +* Implementations of XxHash32 for generating 32-bit hashes, XxHash3 and XxHash64 for generating 64-bit hashes, and xxHash128 for generating 128-bit hashes. ## How to Use +Creating hash codes is straightforward. +Call the `Hash` method with the content to be hashed. + +Here is a practical example: + +```csharp +using System; +using System.IO.Hashing; + +byte[] data = new byte[] { 1, 2, 3, 4 }; + +byte[] crc32Value = Crc32.Hash(data); +Console.WriteLine($"CRC-32 Hash: {BitConverter.ToString(crc32Value)}"); +// CRC-32 Hash: CD-FB-3C-B6 + +byte[] crc64Value = Crc64.Hash(data); +Console.WriteLine($"CRC-64 Hash: {BitConverter.ToString(crc64Value)}"); +// CRC-64 Hash: 58-8D-5A-D4-2A-70-1D-B2 + +byte[] xxHash3Value = XxHash3.Hash(data); +Console.WriteLine($"XxHash3 Hash: {BitConverter.ToString(xxHash3Value)}"); +// XxHash3 Hash: 98-8B-7B-90-33-AC-46-22 + +byte[] xxHash32Value = XxHash32.Hash(data); +Console.WriteLine($"XxHash32 Hash: {BitConverter.ToString(xxHash32Value)}"); +// XxHash32 Hash: FE-96-D1-9C + +byte[] xxHash64Value = XxHash64.Hash(data); +Console.WriteLine($"XxHash64 Hash: {BitConverter.ToString(xxHash64Value)}"); +// XxHash64 Hash: 54-26-20-E3-A2-A9-2E-D1 + +byte[] xxHash128Value = XxHash128.Hash(data); +Console.WriteLine($"XxHash128 Hash: {BitConverter.ToString(xxHash128Value)}"); +// XxHash128 Hash: 49-A0-48-99-59-7A-35-67-53-76-53-A0-D9-95-5B-86 +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.IO.Hashing.Crc32` +* `System.IO.Hashing.Crc64` +* `System.IO.Hashing.XxHash3` +* `System.IO.Hashing.XxHash32` +* `System.IO.Hashing.XxHash64` +* `System.IO.Hashing.XxHash128` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.io.hashing) +* [xxHash - Extremely fast hash algorithm](https://github.com/Cyan4973/xxHash/blob/release/doc/xxhash_spec.md) ## Related Packages +Cryptographic services, including secure encryption and decryption of data: [System.Security.Cryptography](https://learn.microsoft.com/dotnet/api/system.security.cryptography) + ## Feedback & Contributing -System.IO.Hashing is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.IO.Hashing is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From fc02f61386980a20e874ddc4d4c48fade49652d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 18 Oct 2023 22:05:14 +0200 Subject: [PATCH 012/189] Provide Microsoft.Extensions.Options.DataAnnotations package readme (#93657) --- .../src/PACKAGE.md | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md index 3bf3280ad4cf1b..368518bbedb313 100644 --- a/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Options.DataAnnotations/src/PACKAGE.md @@ -2,43 +2,74 @@ +Microsoft.Extensions.Options.DataAnnotations is a library that adds extra validation functionality to configuration options using data annotations. +It allows to apply validation rules to configuration classes to ensure they are correctly configured before the application starts running. + +This way, misconfiguration issues are catched early during the application startup rather than facing them later in production. ## Key Features -* -* -* +* Enables validation of configuration options using data annotations. +* Early detection of misconfiguration issues during application startup. ## How to Use +While configuring services, chain the `ValidateDataAnnotations()` and `ValidateOnStart()` methods to the `AddOptions` method for your configuration class. + +Here is a simple example demonstrating how to validate options on application startup: + +```csharp +services + .AddOptions() + .ValidateDataAnnotations() + .ValidateOnStart(); +``` + +In the configuration class, use data annotations to specify the validation rules. + +For instance, in the following `MyOptions` class, the `Name` property is marked as required: + +```csharp +using System.ComponentModel.DataAnnotations; + +public class MyOptions +{ + [Required(AllowEmptyStrings = false)] + public string Name { get; set; } +} +``` + +With this setup, an error indicating that the `Name` field is required will be thrown upon startup if it hasn't been configured. + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.Options.DataAnnotationsValidateOptions` +* `Microsoft.Extensions.DependencyInjection.OptionsBuilderDataAnnotationsExtensions` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/core/extensions/options) +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.options.dataannotationvalidateoptions-1) ## Related Packages +Core options: [Microsoft.Extensions.Options](https://www.nuget.org/packages/Microsoft.Extensions.Options/) + ## Feedback & Contributing -Microsoft.Extensions.Options.DataAnnotations is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Extensions.Options.DataAnnotations is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 6959db94f1647af44519a9d830de856e7fd8e46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Thu, 19 Oct 2023 21:37:30 +0200 Subject: [PATCH 013/189] Provide Microsoft.Extensions.Caching.Abstractions package readme (#93653) * Provide Caching.Abstractions package readme * Process feedback --- .../src/PACKAGE.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md index 651e3c04c98405..eb8a9beacbc44b 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/PACKAGE.md @@ -2,43 +2,52 @@ +Provides the abstractions to create and use in-memory and distributed caching in your applications. +This library defines how in-memory and distributed caches should be implemented; it doesn’t contain any cache implementation. +With the abstractions provided in this library, various types of caches can be built and used interchangeably, whether the data is kept in memory, in files, or even across a network. ## Key Features -* -* -* +* Interfaces for building and using in-memory and distributed caches. ## How to Use +This package is typically used with an implementation of the caching abstractions, such as `Microsoft.Extensions.Caching.Memory` or `Microsoft.Extensions.Caching.SqlServer`. + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.Caching.Abstractions.ICacheEntry` +* `Microsoft.Extensions.Caching.Abstractions.IMemoryCache` +* `Microsoft.Extensions.Caching.Abstractions.IDistributedCache` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/core/extensions/caching) +* API documentation + * [Microsoft.Extensions.Caching.Memory](https://learn.microsoft.com/dotnet/api/microsoft.extensions.caching.memory) + * [Microsoft.Extensions.Caching.Distributed](https://learn.microsoft.com/dotnet/api/microsoft.extensions.caching.distributed) ## Related Packages +* In-memory caching: [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/) +* SQL Server caching: [Microsoft.Extensions.Caching.SqlServer](https://www.nuget.org/packages/Microsoft.Extensions.Caching.SqlServer/) +* Redis caching: [Microsoft.Extensions.Caching.StackExchangeRedis](https://www.nuget.org/packages/Microsoft.Extensions.Caching.StackExchangeRedis/) + ## Feedback & Contributing -Microsoft.Extensions.Caching.Abstractions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Extensions.Caching.Abstractions is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 298770700c0378eecb14cfaab7644968190d76c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Thu, 19 Oct 2023 21:42:26 +0200 Subject: [PATCH 014/189] Provide System.Memory.Data package readme (#93516) * Provide System.Memory.Data package readme * Update src/libraries/System.Memory.Data/src/PACKAGE.md --------- Co-authored-by: Viktor Hofer --- .../System.Memory.Data/src/PACKAGE.md | 84 ++++++++++++++++--- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Memory.Data/src/PACKAGE.md b/src/libraries/System.Memory.Data/src/PACKAGE.md index 9c4f30806907e1..33996a16b08252 100644 --- a/src/libraries/System.Memory.Data/src/PACKAGE.md +++ b/src/libraries/System.Memory.Data/src/PACKAGE.md @@ -2,43 +2,101 @@ +System.Memory.Data introduces the `BinaryData` type, a lightweight abstraction for a byte payload. +It makes it easy to convert between string, bytes, and stream. + +This abstraction can simplify the API surface by exposing a single type instead of numerous overloads or properties. +The `BinaryData` type handles data ownership efficiently, wrapping passed-in bytes when using `byte[]` or `ReadOnlyMemory` constructors or methods, and managing data as bytes when dealing with streams, strings, or rich model types serialized as JSON. ## Key Features -* -* -* +* Lightweight abstraction for byte payload via `BinaryData` type. +* Convenient helper methods for common conversions among string, bytes, and stream. +* Efficient data ownership handling. ## How to Use +To/From String: + +```csharp +var data = new BinaryData("some data"); + +// ToString will decode the bytes using UTF-8 +Console.WriteLine(data.ToString()); // prints "some data" +``` + +To/From Bytes: + +```csharp +byte[] bytes = Encoding.UTF8.GetBytes("some data"); + +// Create BinaryData using a constructor ... +BinaryData data = new BinaryData(bytes); + +// Or using a static factory method. +data = BinaryData.FromBytes(bytes); + +// There is an implicit cast defined for ReadOnlyMemory +ReadOnlyMemory rom = data; + +// There is also an implicit cast defined for ReadOnlySpan +ReadOnlySpan ros = data; + +// there is also a ToMemory method that gives access to the ReadOnlyMemory. +rom = data.ToMemory(); + +// and a ToArray method that converts into a byte array. +byte[] array = data.ToArray(); +``` + +To/From stream: + +```csharp +var bytes = Encoding.UTF8.GetBytes("some data"); +Stream stream = new MemoryStream(bytes); +var data = BinaryData.FromStream(stream); + +// Calling ToStream will give back a stream that is backed by ReadOnlyMemory, so it is not writable. +stream = data.ToStream(); +Console.WriteLine(stream.CanWrite); // prints false +``` + +`BinaryData` also can be used to integrate with `ObjectSerializer`. +By default, the `JsonObjectSerializer` will be used, but any serializer deriving from `ObjectSerializer` can be used. + +```csharp +var model = new CustomModel +{ + A = "some text", + B = 5, + C = true +}; + +var data = BinaryData.FromObjectAsJson(model); +model = data.ToObjectFromJson(); +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.BinaryData` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/dotnet/api/system.binarydata) ## Feedback & Contributing -System.Memory.Data is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.Memory.Data is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 3f41ed384c5ab1fa478bf5d7f1667c28e106c2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Fri, 20 Oct 2023 00:05:04 +0200 Subject: [PATCH 015/189] Provide System.Security.Cryptography.ProtectedData package readme (#93660) * Provide Cryptography.ProtectedData package readme * Processing review comments * Remove paragraph and improve sentence * Improve code sample --- .../src/PACKAGE.md | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md index 09e76336690163..7a0f751326f550 100644 --- a/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.ProtectedData/src/PACKAGE.md @@ -2,43 +2,71 @@ +System.Security.Cryptography.ProtectedData offers a simplified interface for utilizing Microsoft Windows DPAPI's [CryptProtectData](https://learn.microsoft.com/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata) and [CryptUnprotectData](https://learn.microsoft.com/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata) functions. +**Note**: Since it relies on Windows DPAPI, this package is only supported on Windows platforms. +For more complex cryptographic operations or cross-platform support, consider the [System.Security.Cryptography](https://learn.microsoft.com/dotnet/api/system.security.cryptography) namespace. ## Key Features -* -* -* +* Built upon the robust and secure Windows Data Protection API (DPAPI). +* Data can be protected either for current process or for any process on the machine. +* Scope of protection can be defined either to the current user or the local machine. ## How to Use +Utilizing this package is quite simple, and it mainly revolves around two methods: `Protect` and `Unprotect`. + +Here, `originalData` is the data you want to protect, `optionalEntropy` is an additional byte array used to increase encryption complexity, and `DataProtectionScope` specifies whether the data protection should apply to the current user or the machine. + +```csharp +using System.Security.Cryptography; +using System.Text; + +byte[] originalData = Encoding.UTF8.GetBytes("This is a secret"); +byte[] optionalEntropy = new byte[64]; +Random.Shared.NextBytes(optionalEntropy); + +// To protect: +byte[] encryptedData = ProtectedData.Protect( + originalData, + optionalEntropy, + DataProtectionScope.CurrentUser); + +// To unprotect: +byte[] decryptedData = ProtectedData.Unprotect( + encryptedData, + optionalEntropy, + DataProtectionScope.CurrentUser); +``` + ## Main Types -The main types provided by this library are: +The main type provided by this library is: -* `` -* `` -* `` +* `System.Security.Cryptography.ProtectedData` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/security/how-to-use-data-protection) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.security.cryptography.protecteddata) ## Related Packages +* PKCS and CMS algorithms: [System.Security.Cryptography.Pkcs](https://www.nuget.org/packages/System.Security.Cryptography.Pkcs/) + ## Feedback & Contributing -System.Security.Cryptography.ProtectedData is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.Security.Cryptography.ProtectedData is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From a1caacf2941012d1fdd28d986a417815874281e3 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 20 Oct 2023 10:13:07 +0200 Subject: [PATCH 016/189] Remove en-us culture code from URIs --- src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md | 2 +- .../src/PACKAGE.md | 4 ++-- .../src/PACKAGE.md | 4 ++-- .../Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md | 2 +- .../Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md | 4 ++-- .../Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md | 4 ++-- .../Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md | 4 ++-- .../Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md | 2 +- .../Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md | 4 ++-- .../Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md | 4 ++-- src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md | 4 ++-- .../Microsoft.XmlSerializer.Generator/src/PACKAGE.md | 4 ++-- src/libraries/System.CodeDom/src/PACKAGE.md | 4 ++-- .../src/PACKAGE.md | 4 ++-- .../System.ComponentModel.Composition/src/PACKAGE.md | 4 ++-- .../System.Composition.AttributedModel/src/PACKAGE.md | 4 ++-- src/libraries/System.Composition.Convention/src/PACKAGE.md | 4 ++-- src/libraries/System.Composition.Hosting/src/PACKAGE.md | 4 ++-- src/libraries/System.Composition.Runtime/src/PACKAGE.md | 4 ++-- src/libraries/System.Composition.TypedParts/src/PACKAGE.md | 4 ++-- src/libraries/System.Composition/src/PACKAGE.md | 4 ++-- .../System.Diagnostics.DiagnosticSource/src/PACKAGE.md | 4 ++-- src/libraries/System.Formats.Cbor/src/PACKAGE.md | 2 +- src/libraries/System.IO.Hashing/src/PACKAGE.md | 2 +- src/libraries/System.IO.Packaging/src/PACKAGE.md | 4 ++-- src/libraries/System.IO.Pipelines/src/PACKAGE.md | 4 ++-- src/libraries/System.Numerics.Tensors/src/PACKAGE.md | 4 ++-- src/libraries/System.Reflection.Context/src/PACKAGE.md | 4 ++-- src/libraries/System.Resources.Extensions/src/PACKAGE.md | 4 ++-- .../System.Runtime.Serialization.Schema/src/PACKAGE.md | 4 ++-- .../System.Security.Cryptography.Cose/src/PACKAGE.md | 4 ++-- .../System.Security.Cryptography.Pkcs/src/PACKAGE.md | 4 ++-- src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md | 4 ++-- src/libraries/System.Security.Permissions/src/PACKAGE.md | 4 ++-- src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md | 4 ++-- src/libraries/System.Text.Encodings.Web/src/PACKAGE.md | 4 ++-- src/libraries/System.Threading.AccessControl/src/PACKAGE.md | 4 ++-- src/libraries/System.Threading.RateLimiting/src/PACKAGE.md | 4 ++-- src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md | 4 ++-- src/libraries/System.Windows.Extensions/src/PACKAGE.md | 4 ++-- 40 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md index 4346252b24386b..215d29e162c4b6 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/PACKAGE.md @@ -32,7 +32,7 @@ The main types provided by this library are: ## Additional Documentation -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/System.Security.Cryptography) +* [API documentation](https://learn.microsoft.com/dotnet/api/System.Security.Cryptography) ## Feedback & Contributing diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md index 79b5aea01302e5..0f8ded5894e88a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md index 122fbecfa2ba91..eb20a0af87eba0 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileProviders.Composite/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md index 433fa943e1172a..25bd9129c3968b 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/PACKAGE.md @@ -42,7 +42,7 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/core/extensions/file-globbing) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/core/extensions/file-globbing) * [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.filesystemglobbing) ## Feedback & Contributing diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md index ffb5501c8568fe..84d1bdaf706e04 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md index 7c38fc44d350f7..eb3cd431b79fcf 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md index 8a67a2888f46db..2d071f613b9f2c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md index 25abc578ba6d36..a58e190ec552b9 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/PACKAGE.md @@ -58,7 +58,7 @@ The main types provided by this library are: -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.tracesource) +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.tracesource) ## Related Packages diff --git a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md index 009758b7e5f78e..dc32241e2f569b 100644 --- a/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md +++ b/src/libraries/Microsoft.NET.WebAssembly.Threading/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md index f74e2de2b1ea32..4e35cf443fad59 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md index 4d4256fedf7446..868a31065bcc09 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md b/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md index b83986018d2e70..fa88d0dd906bb8 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md +++ b/src/libraries/Microsoft.XmlSerializer.Generator/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.CodeDom/src/PACKAGE.md b/src/libraries/System.CodeDom/src/PACKAGE.md index 259e0feb50cece..cb86c126345729 100644 --- a/src/libraries/System.CodeDom/src/PACKAGE.md +++ b/src/libraries/System.CodeDom/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md b/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md index 2fa0eb6196f3c2..e78dbe81a51775 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md +++ b/src/libraries/System.ComponentModel.Composition.Registration/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md b/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md index 0096b2acdca3fa..348d4638c5d818 100644 --- a/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md +++ b/src/libraries/System.ComponentModel.Composition/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md b/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md index 1f533435c4b860..50bae9a7bde0ea 100644 --- a/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md +++ b/src/libraries/System.Composition.AttributedModel/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Composition.Convention/src/PACKAGE.md b/src/libraries/System.Composition.Convention/src/PACKAGE.md index 8cad9d889483b0..2d833849f83667 100644 --- a/src/libraries/System.Composition.Convention/src/PACKAGE.md +++ b/src/libraries/System.Composition.Convention/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Composition.Hosting/src/PACKAGE.md b/src/libraries/System.Composition.Hosting/src/PACKAGE.md index be4b5f096e0e07..cfe68edeb2bd65 100644 --- a/src/libraries/System.Composition.Hosting/src/PACKAGE.md +++ b/src/libraries/System.Composition.Hosting/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Composition.Runtime/src/PACKAGE.md b/src/libraries/System.Composition.Runtime/src/PACKAGE.md index 3e30bc15c68321..56964aa6f972eb 100644 --- a/src/libraries/System.Composition.Runtime/src/PACKAGE.md +++ b/src/libraries/System.Composition.Runtime/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Composition.TypedParts/src/PACKAGE.md b/src/libraries/System.Composition.TypedParts/src/PACKAGE.md index cf5a4d1cb02ed9..4fae4f802fe6e3 100644 --- a/src/libraries/System.Composition.TypedParts/src/PACKAGE.md +++ b/src/libraries/System.Composition.TypedParts/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Composition/src/PACKAGE.md b/src/libraries/System.Composition/src/PACKAGE.md index 33a8905040b5f0..d53901f84e77d7 100644 --- a/src/libraries/System.Composition/src/PACKAGE.md +++ b/src/libraries/System.Composition/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md b/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md index 69b5def89a51cb..064ad33b5dd6d7 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Formats.Cbor/src/PACKAGE.md b/src/libraries/System.Formats.Cbor/src/PACKAGE.md index ff26f0debbdc0a..b6549d0941e8c2 100644 --- a/src/libraries/System.Formats.Cbor/src/PACKAGE.md +++ b/src/libraries/System.Formats.Cbor/src/PACKAGE.md @@ -86,7 +86,7 @@ The main types provided by this library are: -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/system.formats.cbor) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.formats.cbor) ## Feedback & Contributing diff --git a/src/libraries/System.IO.Hashing/src/PACKAGE.md b/src/libraries/System.IO.Hashing/src/PACKAGE.md index 64fa5ff754845b..41b90205eac84e 100644 --- a/src/libraries/System.IO.Hashing/src/PACKAGE.md +++ b/src/libraries/System.IO.Hashing/src/PACKAGE.md @@ -8,7 +8,7 @@ Hash code algorithms are pivotal for generating unique values for objects based The namespace encompasses algorithms like CRC-32, CRC-64, xxHash3, xxHash32, xxHash64, and xxHash128, all engineered for swift and efficient hash code generation, with xxHash being an "Extremely fast hash algorithm". **Warning**: The hash functions provided by System.IO.Hashing are not suitable for security purposes such as handling passwords or verifying untrusted content. -For such security-critical applications, consider using cryptographic hash functions provided by the [System.Security.Cryptography](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography) namespace. +For such security-critical applications, consider using cryptographic hash functions provided by the [System.Security.Cryptography](https://learn.microsoft.com/dotnet/api/system.security.cryptography) namespace. ## Key Features diff --git a/src/libraries/System.IO.Packaging/src/PACKAGE.md b/src/libraries/System.IO.Packaging/src/PACKAGE.md index 8ada821aaf440b..cd1d29d8a0e0e8 100644 --- a/src/libraries/System.IO.Packaging/src/PACKAGE.md +++ b/src/libraries/System.IO.Packaging/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.IO.Pipelines/src/PACKAGE.md b/src/libraries/System.IO.Pipelines/src/PACKAGE.md index 97b03f5e636c6a..2bf8184f1c3b65 100644 --- a/src/libraries/System.IO.Pipelines/src/PACKAGE.md +++ b/src/libraries/System.IO.Pipelines/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md index 22d1b422ffccf0..0e72dd30f59d36 100644 --- a/src/libraries/System.Numerics.Tensors/src/PACKAGE.md +++ b/src/libraries/System.Numerics.Tensors/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Reflection.Context/src/PACKAGE.md b/src/libraries/System.Reflection.Context/src/PACKAGE.md index 74ea38d8033bca..c70706d4e8771a 100644 --- a/src/libraries/System.Reflection.Context/src/PACKAGE.md +++ b/src/libraries/System.Reflection.Context/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Resources.Extensions/src/PACKAGE.md b/src/libraries/System.Resources.Extensions/src/PACKAGE.md index 714d6c3fa8d4b6..63fabb28dc0928 100644 --- a/src/libraries/System.Resources.Extensions/src/PACKAGE.md +++ b/src/libraries/System.Resources.Extensions/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md b/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md index cbc39f05fa9464..d0a538498d73cf 100644 --- a/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md +++ b/src/libraries/System.Runtime.Serialization.Schema/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md index f84f8af4d21079..82b8df2029e39f 100644 --- a/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.Cose/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md index 77e903580c6789..4c296584a45bc0 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md b/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md index 9822f0f06533af..6d69a5ce8bc5ca 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md +++ b/src/libraries/System.Security.Cryptography.Xml/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Security.Permissions/src/PACKAGE.md b/src/libraries/System.Security.Permissions/src/PACKAGE.md index ff4038b86dec7b..3a1d7d40d78737 100644 --- a/src/libraries/System.Security.Permissions/src/PACKAGE.md +++ b/src/libraries/System.Security.Permissions/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md index 5d99b5df6e9423..81fff57aba6514 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md +++ b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md index 2c1dbff0851d5c..acf511b4be5a01 100644 --- a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md +++ b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md index eae4bdfe0f4ca8..fd3a0e78cf295d 100644 --- a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md +++ b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md index c1078de92361b0..381d588124a3d7 100644 --- a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md +++ b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md b/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md index c321c68a298166..f15811be3267ab 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages diff --git a/src/libraries/System.Windows.Extensions/src/PACKAGE.md b/src/libraries/System.Windows.Extensions/src/PACKAGE.md index 912da7dffa9a69..edf526dfe90992 100644 --- a/src/libraries/System.Windows.Extensions/src/PACKAGE.md +++ b/src/libraries/System.Windows.Extensions/src/PACKAGE.md @@ -30,8 +30,8 @@ The main types provided by this library are: -* [Conceptual documentation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/en-us/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) +* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) ## Related Packages From bd7cb94a7a3f48c674f706a9e529ac683eee5275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Thu, 2 Nov 2023 05:04:57 +0100 Subject: [PATCH 017/189] Provide Extensions.Hosting.Systemd package readme (#94055) --- .../src/PACKAGE.md | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md index 84d1bdaf706e04..97a4dd4e2f67f8 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/src/PACKAGE.md @@ -2,43 +2,66 @@ +Provides an implementation for hosting an application as a Linux *systemd* service. +The package ensures proper communication between .NET applications and *systemd*, making it easier to build, deploy, and run applications as Linux services. ## Key Features -* -* -* +* Systemd service integration +* Health check support +* Logging and diagnostics with different log levels +* Notify systemd on application state changes ## How to Use +Wherever you configure your host, add the `UseSystemd` method to the builder chain: + +```csharp +public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSystemd() // Enable running as a Systemd service + .ConfigureServices((hostContext, services) => + { + ... + }); +``` + +The `UseSystemd` method will no-op when not running as a daemon, allowing normal debugging or production use both with and without *systemd*. + +Registering the service requires a special file, called a unit file, to be added to the `/etc/systemd/system` directory. + +For more information on this part, check the [run your app as a Linux service with systemd](https://learn.microsoft.com/dotnet/architecture/grpc-for-wcf-developers/self-hosted#run-your-app-as-a-linux-service-with-systemd) guide. + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Extensions.Hosting.Systemd.SystemdNotifier` +* `Microsoft.Extensions.Hosting.Systemd.SystemdLifetime` +* `Microsoft.Extensions.Hosting.SystemdHostBuilderExtensions` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/architecture/grpc-for-wcf-developers/self-hosted#run-your-app-as-a-linux-service-with-systemd) +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.extensions.hosting.systemd) ## Related Packages +* [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) + ## Feedback & Contributing -Microsoft.Extensions.Hosting.Systemd is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Extensions.Hosting.Systemd is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 3c2f2a9a74aad91ed8264c5bc84b6dc25c6bcbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Fri, 3 Nov 2023 08:56:19 +0100 Subject: [PATCH 018/189] Provide M.W.Registry.AccessControl package readme (#94321) --- .../src/PACKAGE.md | 56 ++++++++++++++----- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md index 4e35cf443fad59..8190854cfa8300 100644 --- a/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md +++ b/src/libraries/Microsoft.Win32.Registry.AccessControl/src/PACKAGE.md @@ -2,43 +2,69 @@ - +Provides support for managing access control lists for `Microsoft.Win32.RegistryKey`. ## Key Features -* -* -* +* Get access control lists for a registry key. +* Get a specific sections of an access control list. +* Set the access control list for a registry key. ## How to Use +```csharp +using Microsoft.Win32; +using System.Security.AccessControl; + +// Open a registry key (or create it if it doesn't exist) +using RegistryKey registryKey = Registry.CurrentUser.CreateSubKey("TestKey"); +if (registryKey == null) +{ + Console.WriteLine("Failed to create or open the registry key."); + return; +} + +// Get the current access control list (ACL) for the registry key +RegistrySecurity registrySecurity = registryKey.GetAccessControl(); +Console.WriteLine("Current Access Control List (ACL):"); +Console.WriteLine(registrySecurity.GetSecurityDescriptorSddlForm(AccessControlSections.Access)); + +// Create a new access rule granting full control to the current user +string currentUser = Environment.UserName; +RegistryAccessRule accessRule = new RegistryAccessRule(currentUser, RegistryRights.FullControl, InheritanceFlags.None, PropagationFlags.None, AccessControlType.Allow); + +// Add the new access rule to the ACL +registrySecurity.AddAccessRule(accessRule); + +// Set the updated ACL on the registry key +registryKey.SetAccessControl(registrySecurity); + +// Get and display the updated ACL for the registry key using the second GetAccessControl overload +RegistrySecurity updatedRegistrySecurity = registryKey.GetAccessControl(AccessControlSections.Access); +Console.WriteLine("Updated Access Control List (ACL):"); +Console.WriteLine(updatedRegistrySecurity.GetSecurityDescriptorSddlForm(AccessControlSections.Access)); +``` + ## Main Types -The main types provided by this library are: +The main type provided by this library is: -* `` -* `` -* `` +* `Microsoft.Win32.RegistryAclExtensions` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.win32.registryaclextensions) ## Feedback & Contributing -Microsoft.Win32.Registry.AccessControl is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Win32.Registry.AccessControl is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From aacb637e6980ae263a6d9251cd947e53564762fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Mon, 6 Nov 2023 13:00:33 +0100 Subject: [PATCH 019/189] Provide Microsoft.Win32.SystemEvents package readme (#94371) * Provide M.Win32.SystemEvents package readme * Update src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md * Update src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md --------- Co-authored-by: Viktor Hofer --- .../src/PACKAGE.md | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md index 868a31065bcc09..ca4d0cc02b8a03 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/PACKAGE.md @@ -2,43 +2,78 @@ - +Provides support for accessing Windows system event notifications, which can be crucial for applications to respond to changes in the system or user environment​. +Through this assembly, applications can subscribe to a set of global system events provided by the `SystemEvents` class, gaining the ability to react to changes like system power mode alterations, user preference modifications, and session switches, among others. ## Key Features -* -* -* +* Access to a set of global system events +* Notification of changes in user preferences +* Notification of system power mode changes +* Notification of session switches ## How to Use +An example of how to use the `SystemEvents` class to react to system changes: + +```csharp +using Microsoft.Win32; + +// Set the SystemEvents class to receive event notification when a user +// preference changes, the palette changes, or when display settings change. +SystemEvents.UserPreferenceChanging += UserPreferenceChanging; +SystemEvents.PaletteChanged += PaletteChanged; +SystemEvents.DisplaySettingsChanged += DisplaySettingsChanged; + +// For demonstration purposes, this application sits idle waiting for events. +Console.WriteLine("This application is waiting for system events."); +Console.WriteLine("Press to terminate this application."); +Console.ReadLine(); + +// This method is called when a user preference changes. +static void UserPreferenceChanging(object sender, UserPreferenceChangingEventArgs e) +{ + Console.WriteLine($"The user preference is changing. Category={e.Category}"); +} + +// This method is called when the palette changes. +static void PaletteChanged(object sender, EventArgs e) +{ + Console.WriteLine("The palette changed."); +} + +// This method is called when the display settings change. +static void DisplaySettingsChanged(object sender, EventArgs e) +{ + Console.WriteLine("The display settings changed."); +} +``` + +In this example, the methods will be invoked whenever the user modifies one of several system settings. + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `Microsoft.Win32.SystemEvents` +* `Microsoft.Win32.PowerModeChangedEventHandler` +* `Microsoft.Win32.SessionEndedEventHandler` +* `Microsoft.Win32.UserPreferenceChangedEventHandler` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/dotnet/api/microsoft.win32.systemevents) ## Feedback & Contributing -Microsoft.Win32.SystemEvents is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +Microsoft.Win32.SystemEvents is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From d7114001b8f1b04b6620a671620a7f93fbac6556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Sat, 11 Nov 2023 21:39:53 +0100 Subject: [PATCH 020/189] Provide System.Text.Encodings.Web package readme (#94637) --- .../System.Text.Encodings.Web/src/PACKAGE.md | 114 ++++++++++++++++-- 1 file changed, 101 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md index acf511b4be5a01..70885b3aae0726 100644 --- a/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md +++ b/src/libraries/System.Text.Encodings.Web/src/PACKAGE.md @@ -2,43 +2,131 @@ +Provides types for encoding and escaping strings for use in JavaScript, HTML, and URLs. +This package is essential for protecting web applications against cross-site scripting (XSS) attacks by safely encoding text, and it offers extensive support for Unicode, allowing fine-grained control over which characters are encoded and which are left unescaped. ## Key Features -* -* -* +* Safe encoders for HTML, JavaScript, and URL strings. +* Extensible to support custom encoding scenarios, including the ability to specify Unicode ranges. +* Helps prevent cross-site scripting (XSS) vulnerabilities. +* Flexible Unicode encoding with support for specifying individual or predefined ranges to cover broader sets of characters, including options to avoid escaping specific language character sets. ## How to Use +### Encoding HTML, JavaScript, and URLs + +```csharp +using System.Text.Encodings.Web; + +string unsafeString = ""; + +// HTML encode the string to safely display it on a web page. +string safeHtml = HtmlEncoder.Default.Encode(unsafeString); +Console.WriteLine(safeHtml); +// <script>alert('XSS Attack!');</script> + +// JavaScript encode the string to safely include it in a JavaScript context. +string safeJavaScript = JavaScriptEncoder.Default.Encode(unsafeString); +Console.WriteLine(safeJavaScript); +// \u003Cscript\u003Ealert(\u0027XSS Attack!\u0027);\u003C/script\u003E + +string urlPart = "user input with spaces and & symbols"; + +// URL encode the string to safely include it in a URL. +string encodedUrlPart = UrlEncoder.Default.Encode(urlPart); +Console.WriteLine(encodedUrlPart); +// user%20input%20with%20spaces%20and%20%26%20symbols +``` + +### Custom Encoding Scenario with Specific Unicode Ranges + +```csharp +using System.Text.Encodings.Web; +using System.Text.Unicode; + +TextEncoderSettings customEncoderSettings = new TextEncoderSettings(); +customEncoderSettings.AllowCharacters('!', '*', '-', '.', '_', '~'); // RFC 3986 unreserved characters +customEncoderSettings.AllowRange(new UnicodeRange('a', 26)); +customEncoderSettings.AllowRange(new UnicodeRange('A', 26)); +customEncoderSettings.AllowRange(new UnicodeRange('0', 10)); + +// Create a URL encoder with the custom settings +UrlEncoder customUrlEncoder = UrlEncoder.Create(customEncoderSettings); + +string customUrlPart = "custom data: (@123!)"; + +// By default, the symbols '(', ')', and '@' are not encoded +string defaultEncoded = UrlEncoder.Default.Encode(customUrlPart); +Console.WriteLine(defaultEncoded); +// custom%20data%3A%20(@123!) + +// Now, the symbols '(', ')', and '@' are also encoded +string customEncoded = customUrlEncoder.Encode(customUrlPart); +Console.WriteLine(customEncoded); +// custom%20data%3A%20%28%40123!%29 +``` + +### Serialization with Specific Unicode Character Sets + +By default Cyrillic characters are encoded as Unicode escape sequences in JSON. + +```json +{ + "Date": "2019-08-01T00:00:00-07:00", + "TemperatureCelsius": 25, + "Summary": "\u0436\u0430\u0440\u043A\u043E" +} +``` + +This can be customized by providing a custom `JavaScriptEncoder` to `JsonSerializerOptions`: + +```csharp +JsonSerializerOptions options = new JsonSerializerOptions +{ + Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic), + WriteIndented = true +}; +jsonString = JsonSerializer.Serialize(weatherForecast, options1); +``` + +```json +{ + "Date": "2019-08-01T00:00:00-07:00", + "TemperatureCelsius": 25, + "Summary": "жарко" +} +``` + +More information about this can be found in the [How to customize character encoding with System.Text.Json](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/character-encoding) article. + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.Text.Encodings.Web.HtmlEncoder` +* `System.Text.Encodings.Web.JavaScriptEncoder` +* `System.Text.Encodings.Web.UrlEncoder` +* `System.Text.Encodings.Web.TextEncoder` +* `System.Text.Encodings.Web.TextEncoderSettings` +* `System.Text.Unicode.UnicodeRange` +* `System.Text.Unicode.UnicodeRanges` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/dotnet/api/system.text.encodings.web) ## Feedback & Contributing -System.Text.Encodings.Web is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.Text.Encodings.Web is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From bfa72d38c71293ce8795d6ac428f68945914deac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 6 Dec 2023 19:31:56 +0100 Subject: [PATCH 021/189] Provide ServiceModel.Syndication package readme (#94638) --- .../src/PACKAGE.md | 109 +++++++++++++++--- 1 file changed, 95 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md index 81fff57aba6514..cde4a0fdb43dc4 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md +++ b/src/libraries/System.ServiceModel.Syndication/src/PACKAGE.md @@ -2,43 +2,124 @@ - +Provides types for generating and consuming RSS and Atom feeds. +It is used for creating and parsing syndication feeds, making it easier to build and integrate web content syndication. ## Key Features -* -* -* +* Easy generation and parsing of RSS and Atom feeds. +* Customizable for different syndication needs. +* Support for both feed reading and writing. ## How to Use +### Creating a Feed + +```csharp +using System.ServiceModel.Syndication; +using System.Xml; + +// Create a new syndication feed +SyndicationFeed feed = new SyndicationFeed( + "Feed Title", + "Feed Description", + new Uri("http://example.com"), + "FeedID", + DateTime.Now); + +// Add items to the feed +SyndicationItem item1 = new SyndicationItem( + "Title1", + "Content1", + new Uri("http://example.com/item1")); +feed.Items = new List { item1 }; + +// Serialize the feed to RSS format +using (XmlWriter writer = XmlWriter.Create("rss.xml")) +{ + Rss20FeedFormatter rssFormatter = new Rss20FeedFormatter(feed); + rssFormatter.WriteTo(writer); +} +``` + +Resulting RSS feed: + +```xml + + + + Feed Title + http://example.com/ + Feed Description + Sat, 11 Nov 2023 18:05:21 +0100 + FeedID + + http://example.com/item1 + Title1 + Content1 + + + +``` + +### Consuming a Feed + +```csharp +using System.ServiceModel.Syndication; +using System.Xml; + +string feedUrl = "https://devblogs.microsoft.com/dotnet/feed/"; +using XmlReader reader = XmlReader.Create(feedUrl); + +// Read the feed using Atom10FeedFormatter. +SyndicationFeed feed = SyndicationFeed.Load(reader); + +Console.WriteLine($"Feed Title: {feed.Title.Text}"); +Console.WriteLine("Feed Items:"); + +// Iterate through the feed items and display the title and a brief summary of each. +foreach (SyndicationItem item in feed.Items) +{ + Console.WriteLine($"Title: {item.Title.Text}"); + Console.WriteLine($"Published Date: {item.PublishDate.DateTime}"); +} + +/* + * This code produces the following output: + * + * Feed Title: .NET Blog + * Feed Items: + * - Title: Join us for the Great .NET 8 Hack + * Published Date: 07/11/2023 18:05:00 + * - Title: The convenience of System.IO + * Published Date: 06/11/2023 18:05:00 + */ +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.ServiceModel.Syndication.SyndicationFeed` +* `System.ServiceModel.Syndication.SyndicationItem` +* `System.ServiceModel.Syndication.Rss20FeedFormatter` +* `System.ServiceModel.Syndication.Atom10FeedFormatter` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [Conceptual documentation](https://learn.microsoft.com/dotnet/framework/wcf/feature-details/how-to-create-a-basic-rss-feed) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.servicemodel.syndication) ## Feedback & Contributing -System.ServiceModel.Syndication is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.ServiceModel.Syndication is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From ae03b5550917dc7754de08488adeb1958a3e1450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Wed, 6 Dec 2023 21:22:18 +0100 Subject: [PATCH 022/189] Provide Threading.AccessControl package readme (#94586) --- .../src/PACKAGE.md | 92 ++++++++++++++++--- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md index fd3a0e78cf295d..5d5fa9b740f70a 100644 --- a/src/libraries/System.Threading.AccessControl/src/PACKAGE.md +++ b/src/libraries/System.Threading.AccessControl/src/PACKAGE.md @@ -2,43 +2,107 @@ - +System.Threading.AccessControl provides types that enable you to control access to threading synchronization primitives. +This includes the ability to control access to Mutexes, Semaphores, and Events using Windows Access Control Lists (ACLs). ## Key Features -* -* -* +* Extension methods to allow ACL modifications on `Mutex`, `Semaphore`, and `EventWaitHandle`. +* Simplified security management for threading synchronization objects. ## How to Use +```csharp +using System.Security.AccessControl; +using System.Security.Principal; + +// Create a string representing the current user. +string user = $"{Environment.UserDomainName}\\{Environment.UserName}"; + +// Create a security object that grants no access +MutexSecurity mutexSecurity = new MutexSecurity(); + +// Add a rule that grants the current user the right to enter or release the mutex +MutexAccessRule rule = new MutexAccessRule(user, MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow); +mutexSecurity.AddAccessRule(rule); + +// Add a rule that denies the current user the right to change permissions on the mutex +rule = new MutexAccessRule(user, MutexRights.ChangePermissions, AccessControlType.Deny); +mutexSecurity.AddAccessRule(rule); + +// Display the rules in the security object +ShowSecurity(mutexSecurity); + +// Add a rule that allows the current user the right to read permissions on the mutex +// This rule is merged with the existing Allow rule +rule = new MutexAccessRule(user, MutexRights.ReadPermissions, AccessControlType.Allow); +mutexSecurity.AddAccessRule(rule); + +// Display the rules in the security object +ShowSecurity(mutexSecurity); + +static void ShowSecurity(MutexSecurity security) +{ + Console.WriteLine("\nCurrent access rules:\n"); + + foreach (MutexAccessRule ar in security.GetAccessRules(true, true, typeof(NTAccount))) + { + Console.WriteLine($" User: {ar.IdentityReference}"); + Console.WriteLine($" Type: {ar.AccessControlType}"); + Console.WriteLine($" Rights: {ar.MutexRights}"); + Console.WriteLine(); + } +} + +/* + * This code example produces output similar to following: + * + * Current access rules: + * + * User: TestDomain\TestUser + * Type: Deny + * Rights: ChangePermissions + * + * User: TestDomain\TestUser + * Type: Allow + * Rights: Modify, Synchronize + * + * + * Current access rules: + * + * User: TestDomain\TestUser + * Type: Deny + * Rights: ChangePermissions + * + * User: TestDomain\TestUser + * Type: Allow + * Rights: Modify, ReadPermissions, Synchronize + */ +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.Threading.EventWaitHandleAcl` +* `System.Threading.MutexAcl` +* `System.Threading.SemaphoreAcl` +* `System.Threading.ThreadingAclExtensions` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) - -## Related Packages - - +* [API documentation](https://learn.microsoft.com/dotnet/api/system.threading?view=dotnet-plat-ext-7.0) ## Feedback & Contributing -System.Threading.AccessControl is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.Threading.AccessControl is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From 0845dbdbcaba11880258f473c8c7c585dc3c3d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Fri, 8 Dec 2023 18:23:58 +0100 Subject: [PATCH 023/189] Provide System.Threading.RateLimiting package readme (#94583) * Provide Threading.RateLimiting package readme --- .../src/PACKAGE.md | 134 ++++++++++++++++-- 1 file changed, 125 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md index 381d588124a3d7..a1f2f3111b5646 100644 --- a/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md +++ b/src/libraries/System.Threading.RateLimiting/src/PACKAGE.md @@ -2,36 +2,152 @@ - +Provides a set of types that enable application developers to control the rate of operations. +This can be used to ensure that applications do not exceed certain limits when interacting with resources or services. ## Key Features -* -* +* Flexible rate-limiting primitives that can be applied to various scenarios. +* Supports token bucket, fixed window, and sliding window strategies. * ## How to Use +This is an example of an HttpClient that does client side rate limiting. + +Define a rate limiter. + +```csharp +internal sealed class ClientSideRateLimitedHandler : DelegatingHandler, IAsyncDisposable +{ + private readonly RateLimiter _rateLimiter; + + public ClientSideRateLimitedHandler(RateLimiter limiter) + : base(new HttpClientHandler()) + { + _rateLimiter = limiter; + } + + // Override the SendAsync method to apply rate limiting. + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + // Try to acquire a token from the rate limiter. + using RateLimitLease lease = await _rateLimiter.AcquireAsync(permitCount: 1, cancellationToken); + + // If a token is acquired, proceed with sending the request. + if (lease.IsAcquired) + { + return await base.SendAsync(request, cancellationToken); + } + + // If no token could be acquired, simulate a 429 Too Many Requests response. + var response = new HttpResponseMessage(HttpStatusCode.TooManyRequests); + + // Add a 'Retry-After' header if the rate limiter provides a retry delay. + if (lease.TryGetMetadata(MetadataName.RetryAfter, out TimeSpan retryAfter)) + { + response.Headers.Add("Retry-After", ((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo)); + } + + return response; + } + + // Implement IAsyncDisposable to allow for asynchronous cleanup of resources. + public async ValueTask DisposeAsync() + { + // Dispose of the rate limiter asynchronously. + await _rateLimiter.DisposeAsync().ConfigureAwait(false); + + // Call the base Dispose method. + Dispose(disposing: false); + + // Suppress finalization. + GC.SuppressFinalize(this); + } + + // Dispose pattern to clean up the rate limiter. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + // Synchronously dispose of the rate limiter if disposing is true. + _rateLimiter.Dispose(); + } + } +} +``` + +Using the rate limiter. + +```csharp +using System.Globalization; +using System.Net; +using System.Threading.RateLimiting; + +// Initialize the rate limiter options. +// TokenLimit: Maximum number of tokens that can be acquired at once. +// QueueProcessingOrder: The order in which queued requests will be processed. +// QueueLimit: Maximum number of queued requests. +// ReplenishmentPeriod: How often tokens are replenished. +// TokensPerPeriod: Number of tokens added each period. +// AutoReplenishment: If true, tokens are replenished automatically in the background. +var options = new TokenBucketRateLimiterOptions +{ + TokenLimit = 4, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 2, + ReplenishmentPeriod = TimeSpan.FromMilliseconds(1), + TokensPerPeriod = 2, + AutoReplenishment = true +}; + +// Create a new instance of the TokenBucketRateLimiter with the defined options. +TokenBucketRateLimiter tokenBucketRateLimiter = new TokenBucketRateLimiter(options); + +// A custom HttpMessageHandler that limits the rate of outgoing HTTP requests. +ClientSideRateLimitedHandler clientsideRateLimitedHandler = new ClientSideRateLimitedHandler(tokenBucketRateLimiter); + +// Create an HttpClient that uses the rate-limited handler. +using HttpClient client = new HttpClient(clientsideRateLimitedHandler); + +// Generate a list of dummy URLs for testing the rate limiter. +var oneHundredUrls = Enumerable.Range(0, 100).Select(i => $"https://example.com?iteration={i:00}"); + +// Issue concurrent HTTP GET requests using the HttpClient. +// The rate limiter will control how many requests are sent based on the defined limits. +await Parallel.ForEachAsync(oneHundredUrls.Take(0..100), async (url, cancellationToken) => +{ + using HttpResponseMessage response = await client.GetAsync(url, cancellationToken); + Console.WriteLine($"URL: {url}, HTTP status code: {response.StatusCode} ({(int)response.StatusCode})"); +}); +``` + ## Main Types The main types provided by this library are: -* `` -* `` -* `` +* `System.Threading.RateLimiting.RateLimiter` +* `System.Threading.RateLimiting.ConcurrencyLimiter` +* `System.Threading.RateLimiting.FixedWindowRateLimiter` +* `System.Threading.RateLimiting.ReplenishingRateLimiter` +* `System.Threading.RateLimiting.SlidingWindowRateLimiter` +* `System.Threading.RateLimiting.TokenBucketRateLimiter` +* `System.Threading.RateLimiting.PartitionedRateLimiter` ## Additional Documentation -* [Conceptual documentation](https://learn.microsoft.com/dotnet/standard/serialization/**LIBRARYNAME**/overview) -* [API documentation](https://learn.microsoft.com/dotnet/api/**LIBRARYNAME**) +* [Conceptual documentation](https://learn.microsoft.com/dotnet/core/extensions/http-ratelimiter) +* [API documentation](https://learn.microsoft.com/dotnet/api/system.threading.ratelimiting) ## Related Packages @@ -41,4 +157,4 @@ The main types provided by this library are: -System.Threading.RateLimiting is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). \ No newline at end of file +System.Threading.RateLimiting is released as open source under the [MIT license](https://licenses.nuget.org/MIT). Bug reports and contributions are welcome at [the GitHub repository](https://github.com/dotnet/runtime). From caf570e591daf778d565d1cb8f1bd29408ea1faf Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 13 Dec 2023 14:16:14 -0800 Subject: [PATCH 024/189] Move to building with the 9.0 SDK and move our ToolCurrent TFM to 9.0 --- Directory.Build.props | 4 ++-- global.json | 4 ++-- src/mono/msbuild/apple/build/AppleBuild.LocalBuild.props | 2 +- src/mono/wasm/shared/build/WasmApp.LocalBuild.props | 2 +- src/tools/illink/src/ILLink.Tasks/LinkTask.cs | 2 +- .../src/ILLink.Tasks/build/Microsoft.NET.ILLink.Tasks.props | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index ab7ccdb43bccf6..3c6e3bd623ecc4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -99,11 +99,11 @@ - 8.0 + 9.0 net$(NetCoreAppToolCurrentVersion) $(NetCoreAppCurrentIdentifier),Version=v$(NetCoreAppToolCurrentVersion) - 8.0 + 9.0 net$(AspNetCoreAppCurrentVersion) net462 diff --git a/global.json b/global.json index 1b3640ef522bc6..5d8b0b630ea6b6 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,11 @@ { "sdk": { - "version": "8.0.100", + "version": "9.0.100-alpha.1.23613.1", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "8.0.100" + "dotnet": "9.0.100-alpha.1.23613.1" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23607.2", diff --git a/src/mono/msbuild/apple/build/AppleBuild.LocalBuild.props b/src/mono/msbuild/apple/build/AppleBuild.LocalBuild.props index 8fe589c70e94fc..ed8be7e3ac38e6 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.LocalBuild.props +++ b/src/mono/msbuild/apple/build/AppleBuild.LocalBuild.props @@ -24,7 +24,7 @@ <_NetCoreAppCurrent>net9.0 $(_NetCoreAppCurrent) - <_NetCoreAppToolCurrent>net8.0 + <_NetCoreAppToolCurrent>net9.0 false diff --git a/src/mono/wasm/shared/build/WasmApp.LocalBuild.props b/src/mono/wasm/shared/build/WasmApp.LocalBuild.props index bc5b28321718a4..848ea1ea92db54 100644 --- a/src/mono/wasm/shared/build/WasmApp.LocalBuild.props +++ b/src/mono/wasm/shared/build/WasmApp.LocalBuild.props @@ -22,7 +22,7 @@ - <_TargetFrameworkForNETCoreTasks>net8.0 + <_TargetFrameworkForNETCoreTasks>net9.0 false true $(_WasmTargetsDir) diff --git a/src/tools/illink/src/ILLink.Tasks/LinkTask.cs b/src/tools/illink/src/ILLink.Tasks/LinkTask.cs index 5ac4ecd46ce677..9fd5c536603ee0 100644 --- a/src/tools/illink/src/ILLink.Tasks/LinkTask.cs +++ b/src/tools/illink/src/ILLink.Tasks/LinkTask.cs @@ -262,7 +262,7 @@ public string ILLinkPath { var taskDirectory = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location); #pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file // IL Linker always runs on .NET Core, even when using desktop MSBuild to host ILLink.Tasks. - _illinkPath = Path.Combine (Path.GetDirectoryName (taskDirectory), "net8.0", "illink.dll"); + _illinkPath = Path.Combine (Path.GetDirectoryName (taskDirectory), "net9.0", "illink.dll"); return _illinkPath; } set => _illinkPath = value; diff --git a/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.Tasks.props b/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.Tasks.props index 5ce7ee188e5046..f624c914ea8ea7 100644 --- a/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.Tasks.props +++ b/src/tools/illink/src/ILLink.Tasks/build/Microsoft.NET.ILLink.Tasks.props @@ -15,10 +15,10 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildThisFileDirectory)Microsoft.NET.ILLink.targets - true - $(MSBuildThisFileDirectory)..\tools\net8.0\ILLink.Tasks.dll + $(MSBuildThisFileDirectory)..\tools\net9.0\ILLink.Tasks.dll $(MSBuildThisFileDirectory)..\tools\net472\ILLink.Tasks.dll $(MSBuildThisFileDirectory)Microsoft.NET.ILLink.Analyzers.props From b8004d10223a49ac6456f6b09864393045abab26 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 15 Dec 2023 12:52:33 +0100 Subject: [PATCH 025/189] Update CompatibilitySuppressions.xml --- .../src/linker/CompatibilitySuppressions.xml | 768 +----------------- 1 file changed, 2 insertions(+), 766 deletions(-) diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml index 1e9b7f7c998d0e..302df58915ede4 100644 --- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml +++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml @@ -1,2296 +1,1532 @@ - + CP0001 T:ILLink.Shared.DataFlow.Box`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.ConditionKind - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.DefaultValueDictionary`2 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.DictionaryLattice`3 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.ForwardDataFlowAnalysis`8 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.IBlock`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.IControlFlowGraph`2 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.IDataFlowState`2 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.IDeepCopyValue`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.ILattice`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.INegate`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.IRegion`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.ITransfer`5 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.Maybe`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.MaybeLattice`2 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.RegionKind - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.SingleValue - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.ValueSet`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DataFlow.ValueSetLattice`1 - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DiagnosticId - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DiagnosticIdExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.DiagnosticString - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.MessageSubCategory - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TrimAnalysis.FieldReferenceValue - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TrimAnalysis.LocalVariableReferenceValue - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TrimAnalysis.ReferenceValue - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TrimAnalysis.ValueWithDynamicallyAccessedMembers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TypeSystemProxy.ParameterIndex - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TypeSystemProxy.ReferenceKind - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TypeSystemProxy.WellKnownType - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:ILLink.Shared.TypeSystemProxy.WellKnownTypeExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.AssemblyDefinitionExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.AssemblyResolver - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.AssemblyRootMode - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.AttributeInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.BannedApiExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.BCL - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.CodeOptimizations - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.CodeOptimizationsSettings - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.ConsoleLogger - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.CustomAttributeArgumentExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.CustomAttributeSource - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.AttributeDataFlow - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.CompilerGeneratedState - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.GenericArgumentDataFlow - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.HoistedLocalKey - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.ReflectionMarker - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.TrimAnalysisAssignmentPattern - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.TrimAnalysisMethodCallPattern - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.TrimAnalysisPatternStore - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.ValueBasicBlockPair - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Dataflow.ValueNodeList - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.DependencyInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.DependencyKind - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.DependencyRecorderHelper - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.DgmlDependencyRecorder - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.DocumentationSignatureGenerator - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.DocumentationSignatureParser - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Driver - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.EmbeddedXmlInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.FeatureSettings - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.IDependencyRecorder - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.ILogger - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.InternalErrorException - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.ITryResolveMetadata - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.IXApiVisitor - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.KnownMembers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.LinkerFatalErrorException - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.LinkerILProcessor - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MarkingHelpers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MemberActionStore - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MemberReferenceExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MetadataTrimming - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MethodBodyScanner - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MethodDefinitionExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MethodIL - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.MethodReferenceExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.ModuleDefinitionExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.OverrideInformation.OverridePair - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.ParameterHelpers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.PInvokeInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Pipeline - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.PlatformAssemblies - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.RemoveAttributeInstancesAttribute - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.SerializationMarker - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.SerializerKind - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.AddBypassNGenStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.AllowedAssemblies - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.BodySubstituterStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.BodySubstitutionParser - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.CheckSuppressionsDispatcher - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.CheckSuppressionsStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.CleanStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.CodeRewriterStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.DescriptorMarker - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.DiscoverOperatorsHandler - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.DiscoverSerializationHandler - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.LinkAttributesParser - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.LinkAttributesStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.MarkExportedTypesTarget - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.MarkScopeStack - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.MarkStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.MarkStepContext - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.OutputStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.OutputWarningSuppressions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ProcessLinkerXmlBase - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ProcessLinkerXmlStepBase - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ProcessReferencesStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ProcessWarningsStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ReflectionBlockedStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.RegenerateGuidStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.RemoveResourcesStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.RemoveSecurity - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ResolveFromXmlStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.RootAssemblyInput - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.SealerStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.SweepStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.UnreachableBlocksOptimizer - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Steps.ValidateVirtualMethodAnnotationsStep - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.SubstitutionInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.SuppressMessageInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.TargetRuntimeVersion - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.Tracer - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.TypeDefinitionExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.TypeMapInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.TypePreserveMembers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.TypeReferenceExtensions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.UnconditionalSuppressMessageAttributeState - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.UnintializedContextFactory - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.WarningSuppressionWriter - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:Mono.Linker.XmlDependencyRecorder - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:System.Reflection.AssemblyNameHelpers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:System.Reflection.Runtime.TypeParsing.AssemblyQualifiedTypeName - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:System.Reflection.Runtime.TypeParsing.NonQualifiedTypeName - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:System.Reflection.Runtime.TypeParsing.TypeName - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:System.Reflection.Runtime.TypeParsing.TypeParser - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0001 T:System.Reflection.RuntimeAssemblyName - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.assembly_actions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.context - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.fieldType_init - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.indirectly_called - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.marked_attributes - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.marked_instantiated - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.marked_pending - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.pending_preserve - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.preserved_exportedtype_members - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.preserved_methods - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.preserved_type_members - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.preserved_types - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.processed - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.public_api - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.resources_to_remove - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.symbol_readers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.AnnotationStore.types_relevant_to_variant_casting - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.LinkContext.PInvokesListFile - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.MessageCategory.WarningAsError - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 F:Mono.Linker.MessageContainer.Empty - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.#ctor(Mono.Linker.LinkContext) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.AddResourceToRemove(Mono.Cecil.AssemblyDefinition,Mono.Cecil.EmbeddedResource) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.AddSymbolReader(Mono.Cecil.AssemblyDefinition,Mono.Cecil.Cil.ISymbolReader) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.ChoosePreserveActionWhichPreservesTheMost(Mono.Linker.TypePreserve,Mono.Linker.TypePreserve) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.ClearPreservedMethods(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.ClearPreservedMethods(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.CloseSymbolReader(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.EnqueueVirtualMethod(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.get_MemberActions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.get_ProcessSatelliteAssemblies - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.get_Tracer - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.get_TypeMapInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetAction(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetAssemblies - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetBaseMethods(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetDefaultInterfaceImplementations(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetLinkerAttributes``1(Mono.Cecil.IMemberDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetMarkedPending - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetPendingPreserve - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetPreservedMethods(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetPreservedMethods(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.GetResourcesToRemove(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.HasAppliedPreserve(Mono.Cecil.TypeDefinition,Mono.Linker.TypePreserve) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.HasLinkerAttribute``1(Mono.Cecil.IMemberDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.HasMarkedAnyIndirectlyCalledMethods - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.HasPreservedStaticCtor(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.HasSubstitutedInit(Mono.Cecil.FieldDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.HasSubstitutedInit(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.IsIndirectlyCalled(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.IsInstantiated(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.IsProcessed(Mono.Cecil.IMetadataTokenProvider) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.IsPublic(Mono.Cecil.IMetadataTokenProvider) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.IsReflectionUsed(Mono.Cecil.IMemberDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.IsRelevantToVariantCasting(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.Mark(Mono.Cecil.CustomAttribute,Mono.Linker.DependencyInfo@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.Mark(Mono.Cecil.IMetadataTokenProvider,Mono.Linker.DependencyInfo@,Mono.Linker.MessageOrigin@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.MarkIndirectlyCalledMethod(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.MarkInstantiated(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.MarkProcessed(Mono.Cecil.IMetadataTokenProvider,Mono.Linker.DependencyInfo@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.MarkReflectionUsed(Mono.Cecil.IMemberDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.MarkRelevantToVariantCasting(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.PrepareDependenciesDump - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.PrepareDependenciesDump(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.set_ProcessSatelliteAssemblies(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetAppliedPreserve(Mono.Cecil.TypeDefinition,Mono.Linker.TypePreserve) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetMembersPreserve(Mono.Cecil.ExportedType,Mono.Linker.TypePreserveMembers) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetMembersPreserve(Mono.Cecil.TypeDefinition,Mono.Linker.TypePreserveMembers) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetPreservedStaticCtor(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetProcessed(Mono.Cecil.IMetadataTokenProvider) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetPublic(Mono.Cecil.IMetadataTokenProvider) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.SetSubstitutedInit(Mono.Cecil.TypeDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.TryGetFieldUserValue(Mono.Cecil.FieldDefinition,System.Object@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.TryGetLinkerAttribute``1(Mono.Cecil.IMemberDefinition,``0@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.TryGetMethodStubValue(Mono.Cecil.MethodDefinition,System.Object@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.TryGetPreserve(Mono.Cecil.TypeDefinition,Mono.Linker.TypePreserve@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.TryGetPreservedMembers(Mono.Cecil.ExportedType,Mono.Linker.TypePreserveMembers@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.AnnotationStore.TryGetPreservedMembers(Mono.Cecil.TypeDefinition,Mono.Linker.TypePreserveMembers@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.#ctor(Mono.Linker.Pipeline,Mono.Linker.ILogger,System.String,Mono.Linker.UnintializedContextFactory) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.#ctor(Mono.Linker.Pipeline,Mono.Linker.ILogger,System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.CalculateAssemblyAction(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.CanApplyOptimization(Mono.Linker.CodeOptimizations,Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.Dispose - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.FatalError(System.String,System.Int32,System.Exception,System.String,System.Nullable{Mono.Linker.MessageOrigin}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.FatalError(System.String,System.Int32,System.String,System.Nullable{Mono.Linker.MessageOrigin}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.FlushCachedWarnings - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_Actions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_AddReflectionAnnotations - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_AssembliesWithGeneratedSingleWarning - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_AssemblyListFile - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_CompilerGeneratedState - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_CustomAttributes - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_DefaultAction - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_DeterministicOutput - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_DisableEventSourceSpecialHandling - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_DisableOperatorDiscovery - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_EmbeddedXmlInfo - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_EnableReducedTracing - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_EnableSerializationDiscovery - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_ErrorsCount - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_FeatureSettings - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_GeneralSingleWarn - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_GeneralWarnAsError - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_IgnoreDescriptors - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_IgnoreLinkAttributes - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_IgnoreSubstitutions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_IgnoreUnresolved - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_KeepMembersForDebugger - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_KeepUsedAttributeTypesOnly - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_LinkSymbols - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_LogMessages - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_MarkedKnownMembers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_MarkHandlers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_MarkingHelpers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_MetadataTrimming - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_NoTrimWarn - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_NoWarn - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_Optimizations - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_OutputDirectory - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_PInvokes - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_Pipeline - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_Resolver - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_SerializationMarker - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_SingleWarn - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_StripSecurity - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_Suppressions - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_SymbolReaderProvider - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_Tracer - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_TrimAction - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_WarnAsError - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_WarningSuppressionWriter - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.get_WarnVersion - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.GetAssemblies - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.GetMethodIL(Mono.Cecil.Cil.MethodBody) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.GetMethodIL(Mono.Cecil.MethodDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.GetReferencedAssemblies - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.GetTargetRuntimeVersion - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.HasFeatureValue(System.String,System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.IsOptimizationEnabled(Mono.Linker.CodeOptimizations,Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.IsOptimizationEnabled(Mono.Linker.CodeOptimizations,Mono.Cecil.MemberReference) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.IsSingleWarn(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.IsTrimmable(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.IsWarningAsError(System.Int32) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.IsWarningSuppressed(System.Int32,System.String,Mono.Linker.MessageOrigin) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogDiagnostic(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogError(System.Nullable{Mono.Linker.MessageOrigin},ILLink.Shared.DiagnosticId,System.String[]) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogError(System.String,System.Int32,System.String,System.Nullable{Mono.Linker.MessageOrigin}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogMessage(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(Mono.Cecil.IMemberDefinition,ILLink.Shared.DiagnosticId,System.Nullable{System.Int32},System.String[]) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(Mono.Cecil.IMemberDefinition,ILLink.Shared.DiagnosticId,System.String[]) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(Mono.Linker.MessageOrigin,ILLink.Shared.DiagnosticId,System.String[]) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(System.String,ILLink.Shared.DiagnosticId,System.String[]) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(System.String,System.Int32,Mono.Cecil.IMemberDefinition,System.Nullable{System.Int32},System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(System.String,System.Int32,Mono.Linker.MessageOrigin,System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.LogWarning(System.String,System.Int32,System.String,System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.RegisterAssembly(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.RegisterAssemblyAction(System.String,Mono.Linker.AssemblyAction) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.ReportUnresolved(Mono.Cecil.ExportedType) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.ReportUnresolved(Mono.Cecil.FieldReference) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.ReportUnresolved(Mono.Cecil.MethodReference) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.ReportUnresolved(Mono.Cecil.TypeReference) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.Resolve(Mono.Cecil.ExportedType) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.Resolve(Mono.Cecil.IMetadataScope) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.ResolveReferences(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.SafeReadSymbols(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.SeenFirstTime(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_AddReflectionAnnotations(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_AssembliesWithGeneratedSingleWarning(System.Collections.Generic.HashSet{System.String}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_AssemblyListFile(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_DefaultAction(Mono.Linker.AssemblyAction) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_DeterministicOutput(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_DisableEventSourceSpecialHandling(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_DisableOperatorDiscovery(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_EnableReducedTracing(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_EnableSerializationDiscovery(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_FeatureSettings(System.Collections.Generic.Dictionary{System.String,System.Boolean}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_GeneralSingleWarn(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_GeneralWarnAsError(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_IgnoreDescriptors(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_IgnoreLinkAttributes(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_IgnoreSubstitutions(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_IgnoreUnresolved(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_KeepMembersForDebugger(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_KeepUsedAttributeTypesOnly(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_LinkSymbols(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_LogMessages(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_MetadataTrimming(Mono.Linker.MetadataTrimming) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_NoTrimWarn(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_NoWarn(System.Collections.Generic.HashSet{System.Int32}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_Optimizations(Mono.Linker.CodeOptimizationsSettings) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_OutputDirectory(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_SingleWarn(System.Collections.Generic.Dictionary{System.String,System.Boolean}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_StripSecurity(System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_Suppressions(Mono.Linker.UnconditionalSuppressMessageAttributeState) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_SymbolReaderProvider(Mono.Cecil.Cil.ISymbolReaderProvider) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_TrimAction(Mono.Linker.AssemblyAction) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_WarnAsError(System.Collections.Generic.Dictionary{System.Int32,System.Boolean}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_WarningSuppressionWriter(Mono.Linker.WarningSuppressionWriter) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.set_WarnVersion(Mono.Linker.WarnVersion) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.SetAction(Mono.Cecil.AssemblyDefinition,Mono.Linker.AssemblyAction) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.SetCustomData(System.String,System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.SetFeatureValue(System.String,System.Boolean) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.TryResolve(Mono.Cecil.AssemblyDefinition,System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.TryResolve(Mono.Cecil.AssemblyNameReference) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.TryResolve(Mono.Cecil.ExportedType) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.LinkContext.TryResolve(System.String) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.CompareTo(Mono.Linker.MessageContainer) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.get_Category - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.get_Code - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.get_Origin - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.get_SubCategory - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.get_Text - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.IsWarningMessage(System.Nullable{System.Int32}@) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.op_Equality(Mono.Linker.MessageContainer,Mono.Linker.MessageContainer) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.op_Inequality(Mono.Linker.MessageContainer,Mono.Linker.MessageContainer) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageContainer.ToMSBuildString - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.#ctor(Mono.Cecil.ICustomAttributeProvider,System.Nullable{System.Int32}) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.#ctor(Mono.Cecil.ICustomAttributeProvider) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.#ctor(Mono.Linker.MessageOrigin,System.Int32) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.#ctor(Mono.Linker.MessageOrigin) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.#ctor(System.String,System.Int32,System.Int32,Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.CompareTo(Mono.Linker.MessageOrigin) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.get_FileName - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.get_ILOffset - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.get_Provider - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.get_SourceColumn - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.get_SourceLine - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.op_Equality(Mono.Linker.MessageOrigin,Mono.Linker.MessageOrigin) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.op_Inequality(Mono.Linker.MessageOrigin,Mono.Linker.MessageOrigin) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.MessageOrigin.WithInstructionOffset(System.Int32) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.OverrideInformation.#ctor(Mono.Cecil.MethodDefinition,Mono.Cecil.MethodDefinition,Mono.Linker.ITryResolveMetadata,Mono.Cecil.InterfaceImplementation) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.OverrideInformation.get_IsOverrideOfInterfaceMember - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.OverrideInformation.get_IsStaticInterfaceMethodPair - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.Steps.BaseStep.get_MarkingHelpers - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.Steps.BaseStep.get_Tracer - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 M:Mono.Linker.Steps.SubStepsDispatcher.Process(Mono.Linker.LinkContext) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0008 T:Mono.Linker.LinkContext - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0008 T:Mono.Linker.MessageContainer - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0008 T:Mono.Linker.MessageOrigin - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0009 T:Mono.Linker.AnnotationStore - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0009 T:Mono.Linker.LinkContext - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0009 T:Mono.Linker.OverrideInformation - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0016 M:Mono.Linker.AnnotationStore.Mark(Mono.Cecil.CustomAttribute):[T:System.ObsoleteAttribute] - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0016 M:Mono.Linker.AnnotationStore.Mark(Mono.Cecil.IMetadataTokenProvider):[T:System.ObsoleteAttribute] - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0016 M:Mono.Linker.LinkContext.TryGetCustomData(System.String,System.String@)$1:[T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute] - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0017 M:Mono.Linker.LinkContext.Resolve(Mono.Cecil.AssemblyNameReference)$0 - ref/net8.0/illink.dll - lib/net8.0/illink.dll - \ No newline at end of file + From ad27b82a37e50d80877296b70cd4130b5757d673 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 15 Dec 2023 13:30:01 +0100 Subject: [PATCH 026/189] Update PathUtilities.cs --- src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs b/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs index cfb7627153307c..7c2d565c241974 100644 --- a/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs +++ b/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs @@ -10,7 +10,9 @@ namespace Mono.Linker.Tests.TestCasesRunner { public static class PathUtilities { -#if NET8_0 +#if NET9_0 + public const string TFMDirectoryName = "net9.0"; +#elif NET8_0 public const string TFMDirectoryName = "net8.0"; #elif NET7_0 public const string TFMDirectoryName = "net7.0"; From 45c577de3947863215ebc343d428da6a811b7364 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 15 Dec 2023 20:45:06 +0100 Subject: [PATCH 027/189] Update TestProjects.targets --- .../Assets/TestUtils/TestProjects.targets | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/installer/tests/Assets/TestUtils/TestProjects.targets b/src/installer/tests/Assets/TestUtils/TestProjects.targets index 712dba87438a4d..60faea7812009d 100644 --- a/src/installer/tests/Assets/TestUtils/TestProjects.targets +++ b/src/installer/tests/Assets/TestUtils/TestProjects.targets @@ -4,29 +4,4 @@ provides basic info needed for restore and build with the vanilla SDK. --> - - - 9.0 - - - - - - - From 708eca84514f3fc88d95c4d2f336a097f3bf5450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 19 Dec 2023 10:24:38 +0100 Subject: [PATCH 028/189] Update hotreload-utils dependencies --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 857d5e8337b345..5f17ab2aa778c7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -353,9 +353,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization fb325047175ea6436e3c2c88f6f56505e0e834c2 - + https://github.com/dotnet/hotreload-utils - cc369887e92bac086dd296beb7cd169b6cff9716 + 14a6f0b652e5d1053b5213adfcdfe1aaa6d52d6d https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index dcd64ca232cb0c..fd5bf655013019 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -182,7 +182,7 @@ 9.0.0-prerelease.23611.1 9.0.0-prerelease.23611.1 9.0.0-prerelease.23611.1 - 9.0.0-alpha.0.23611.1 + 9.0.0-alpha.0.23618.4 3.12.0 4.1.0 6.0.0 From b134b946cc1a7b3178dbd30b8042884582418c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 19 Dec 2023 10:31:47 +0100 Subject: [PATCH 029/189] Bump sdk version --- global.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/global.json b/global.json index 96a68e1ba8cd25..3f2d3ca7be9229 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,11 @@ { "sdk": { - "version": "9.0.100-alpha.1.23613.1", + "version": "9.0.100-alpha.1.23615.4", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "9.0.100-alpha.1.23613.1" + "dotnet": "9.0.100-alpha.1.23615.4" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.23607.2", From b5aecc4f29da1051b2154f6377dc6921ca93e98d Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 19 Dec 2023 12:22:04 +0100 Subject: [PATCH 030/189] Update ILLink.RoslynAnalyzer.Tests.csproj --- .../ILLink.RoslynAnalyzer.Tests.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj index 1c55448119bd8e..d4ff25c9ae1aca 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj @@ -36,15 +36,9 @@ $(LinkerTestDir) - - $(ArtifactsDir) - $(ArtifactsBinDir) - - $(TargetArchitecture) - From e66999a6351107d2b85907a48f5901f14440bd99 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 19 Dec 2023 12:22:27 +0100 Subject: [PATCH 031/189] Update Mono.Linker.Tests.csproj --- .../Mono.Linker.Tests.csproj | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj b/src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj index 87c955998023b0..26f5a5b6cf6d4e 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj +++ b/src/tools/illink/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj @@ -6,14 +6,6 @@ NUnit - - - - <_Parameter1>CecilPackageVersion - <_Parameter2>$(MicrosoftDotNetCecilVersion) - - - @@ -41,15 +33,9 @@ PreserveNewest - - $(RepoRoot) - $(Configuration) - - $(ArtifactsDir) - $(CoreCLRArtifactsPath) @@ -59,6 +45,13 @@ $(LinkerTestDir) + + $(TargetFramework) + + + + $(MicrosoftDotNetCecilVersion) + From c98d3e4d92baafbfe39fd1aa2d83ae14e55072a6 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 19 Dec 2023 12:22:45 +0100 Subject: [PATCH 032/189] Update CecilVersionCheck.cs --- .../Tests/CecilVersionCheck.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests/Tests/CecilVersionCheck.cs b/src/tools/illink/test/Mono.Linker.Tests/Tests/CecilVersionCheck.cs index 538bff2bf46505..909d0d410c633b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/Tests/CecilVersionCheck.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/Tests/CecilVersionCheck.cs @@ -12,19 +12,17 @@ namespace Mono.Linker.Tests public class CecilVersionCheck { [TestCase] - public void CecilPackageVersionMatchesAssemblyVersion () + public void CecilPackageVersionMatchesAssemblyVersion() { - var thisAssembly = Assembly.GetExecutingAssembly (); - var cecilPackageVersion = thisAssembly - .GetCustomAttributes () - .Where (ca => ca.Key == "CecilPackageVersion") - .Single ().Value; + string cecilPackageVersion = (string)AppContext.GetData("Mono.Linker.Tests.CecilPackageVersion")!; // Assume that the test assembly builds against the same cecil as ILLink. - var cecilAssemblyVersion = thisAssembly - .GetReferencedAssemblies () - .Where (an => an.Name == "Mono.Cecil") - .Single ().Version; + var cecilAssemblyVersion = Assembly + .GetExecutingAssembly() + .GetReferencedAssemblies() + .Single(an => an.Name == "Mono.Cecil") + .Version; + Assert.AreEqual(cecilPackageVersion.AsSpan(0,6).ToString(), cecilAssemblyVersion.ToString(3)); } } -} \ No newline at end of file +} From e7c5aeaa766ba01813a35c1182aef03819853ef4 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 19 Dec 2023 12:23:13 +0100 Subject: [PATCH 033/189] Update PathUtilities.cs --- .../Trimming.Tests.Shared/PathUtilities.cs | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs b/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs index 7c2d565c241974..b74ea18e7e74d0 100644 --- a/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs +++ b/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #nullable enable + using System; using System.IO; using System.Runtime.CompilerServices; @@ -10,32 +11,16 @@ namespace Mono.Linker.Tests.TestCasesRunner { public static class PathUtilities { -#if NET9_0 - public const string TFMDirectoryName = "net9.0"; -#elif NET8_0 - public const string TFMDirectoryName = "net8.0"; -#elif NET7_0 - public const string TFMDirectoryName = "net7.0"; -#elif NET6_0 - public const string TFMDirectoryName = "net6.0"; -#elif NET5_0 - public const string TFMDirectoryName = "net5.0"; -#elif NETCOREAPP3_0 - public const string TFMDirectoryName = "netcoreapp3.0"; -#elif NET471 - public const string TFMDirectoryName = "net471"; -#else -#error "Unknown TFM" -#endif - - public static string GetTestsSourceRootDirectory ([CallerFilePath] string? thisFile = null) => - Path.GetFullPath ((string) AppContext.GetData ("Mono.Linker.Tests.LinkerTestDir")!); + public static string GetTestsSourceRootDirectory([CallerFilePath] string? thisFile = null) => + Path.GetFullPath((string)AppContext.GetData("Mono.Linker.Tests.LinkerTestDir")!); - public static string GetTestAssemblyRoot (string assemblyName) + public static string GetTestAssemblyRoot(string assemblyName) { - var artifactsBinDirectory = (string) AppContext.GetData ("Mono.Linker.Tests.ArtifactsBinDir")!; - var configuration = (string) AppContext.GetData ("Mono.Linker.Tests.Configuration")!; - return Path.GetFullPath (Path.Combine (artifactsBinDirectory, assemblyName, configuration, TFMDirectoryName)); + string artifactsBinDirectory = (string)AppContext.GetData("Mono.Linker.Tests.ArtifactsBinDir")!; + string configuration = (string)AppContext.GetData("Mono.Linker.Tests.Configuration")!; + string targetFramework = (string)AppContext.GetData("Mono.Linker.Tests.TargetFramework")!; + + return Path.GetFullPath(Path.Combine(artifactsBinDirectory, assemblyName, configuration, targetFramework)); } } } From 1d3d9ec73d54a44bd75ec5e6546bcea3dab8e277 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 19 Dec 2023 12:45:29 +0100 Subject: [PATCH 034/189] Update PathUtilities.cs --- .../illink/test/Trimming.Tests.Shared/PathUtilities.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs b/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs index b74ea18e7e74d0..051a6f1a7ac6ae 100644 --- a/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs +++ b/src/tools/illink/test/Trimming.Tests.Shared/PathUtilities.cs @@ -11,16 +11,17 @@ namespace Mono.Linker.Tests.TestCasesRunner { public static class PathUtilities { - public static string GetTestsSourceRootDirectory([CallerFilePath] string? thisFile = null) => + public static string TFMDirectoryName => (string)AppContext.GetData("Mono.Linker.Tests.TargetFramework")!; + + public static string GetTestsSourceRootDirectory([CallerFilePath]string? thisFile = null) => Path.GetFullPath((string)AppContext.GetData("Mono.Linker.Tests.LinkerTestDir")!); public static string GetTestAssemblyRoot(string assemblyName) { string artifactsBinDirectory = (string)AppContext.GetData("Mono.Linker.Tests.ArtifactsBinDir")!; string configuration = (string)AppContext.GetData("Mono.Linker.Tests.Configuration")!; - string targetFramework = (string)AppContext.GetData("Mono.Linker.Tests.TargetFramework")!; - return Path.GetFullPath(Path.Combine(artifactsBinDirectory, assemblyName, configuration, targetFramework)); + return Path.GetFullPath(Path.Combine(artifactsBinDirectory, assemblyName, configuration, TFMDirectoryName)); } } } From 47572ac3f1bcd8455386746ea4c437509db70b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Tue, 19 Dec 2023 12:02:14 +0100 Subject: [PATCH 035/189] Update SourceBuildPrebuiltBaseline.xml --- eng/SourceBuildPrebuiltBaseline.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index a96dee9c6454e6..85e4ba20b628b1 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -10,12 +10,12 @@ - - - + + + - + + + + + + + diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs index c00a692e94c915..ae1206588b0f88 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs @@ -45,8 +45,7 @@ static Task VerifyRequiresAssemblyFilesCodeFix ( { var test = new VerifyCS.Test { TestCode = source, - FixedCode = fixedSource, - ReferenceAssemblies = TestCaseUtils.NetCoreAppReferencessemblies + FixedCode = fixedSource }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs index a42fa27c14e07b..ecc5e2c3e7c98e 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs @@ -43,8 +43,7 @@ static Task VerifyRequiresDynamicCodeCodeFix ( { var test = new VerifyCS.Test { TestCode = source + dynamicCodeAttribute, - FixedCode = fixedSource + dynamicCodeAttribute, - ReferenceAssemblies = TestCaseUtils.NetCoreAppReferencessemblies + FixedCode = fixedSource + dynamicCodeAttribute }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 95a92db83694a4..294b1ea9405df7 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -39,8 +39,7 @@ static Task VerifyRequiresUnreferencedCodeCodeFix ( { var test = new VerifyCS.Test { TestCode = source, - FixedCode = fixedSource, - ReferenceAssemblies = TestCaseUtils.NetCoreAppReferencessemblies + FixedCode = fixedSource }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs index cc5655515e6990..1a51ba84083287 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseCompilation.cs @@ -22,7 +22,7 @@ internal static class TestCaseCompilation new RequiresUnreferencedCodeAnalyzer (), new DynamicallyAccessedMembersAnalyzer ()); - public static Task<(CompilationWithAnalyzers Compilation, SemanticModel SemanticModel, List ExceptionDiagnostics)> CreateCompilation ( + public static (CompilationWithAnalyzers Compilation, SemanticModel SemanticModel, List ExceptionDiagnostics) CreateCompilation ( string src, bool consoleApplication, (string, string)[]? globalAnalyzerOptions = null, @@ -31,7 +31,7 @@ internal static class TestCaseCompilation IEnumerable? additionalFiles = null) => CreateCompilation (CSharpSyntaxTree.ParseText (src), consoleApplication, globalAnalyzerOptions, additionalReferences, additionalSources, additionalFiles); - public static async Task<(CompilationWithAnalyzers Compilation, SemanticModel SemanticModel, List ExceptionDiagnostics)> CreateCompilation ( + public static (CompilationWithAnalyzers Compilation, SemanticModel SemanticModel, List ExceptionDiagnostics) CreateCompilation ( SyntaxTree src, bool consoleApplication, (string, string)[]? globalAnalyzerOptions = null, @@ -46,7 +46,7 @@ internal static class TestCaseCompilation var comp = CSharpCompilation.Create ( assemblyName: Guid.NewGuid ().ToString ("N"), syntaxTrees: sources, - references: (await TestCaseUtils.GetDotNetReferences ()).Add (mdRef).AddRange (additionalReferences), + references: SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences().Add(mdRef).AddRange(additionalReferences), new CSharpCompilationOptions (consoleApplication ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary)); var analyzerOptions = new AnalyzerOptions ( additionalFiles: additionalFiles?.ToImmutableArray () ?? ImmutableArray.Empty, diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs index c8f5bbd9c424e7..d05326239a39e5 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; using Mono.Linker.Tests.Cases.Expectations.Metadata; +using SourceGenerators.Tests; using Xunit; namespace ILLink.RoslynAnalyzer.Tests @@ -24,25 +25,6 @@ public abstract class TestCaseUtils { private static readonly string MonoLinkerTestsCases = "Mono.Linker.Tests.Cases"; - private static string targetFramework = (string)AppContext.GetData("ILLink.RoslynAnalyzer.Tests.TargetFramework")!; - - public static readonly ReferenceAssemblies NetCoreAppReferencessemblies = - new ReferenceAssemblies ( - targetFramework, - new PackageIdentity ("Microsoft.NETCore.App.Ref", $"net{Environment.Version.Major}.{Environment.Version.Minor}"), - Path.Combine ("ref", targetFramework)) - .WithNuGetConfigFilePath (Path.Combine (TestCaseUtils.GetRepoRoot (), "NuGet.config")); - - private static ImmutableArray s_netcoreappRefs; - public static async ValueTask> GetDotNetReferences () - { - if (s_netcoreappRefs.IsDefault) { - var refs = await NetCoreAppReferencessemblies.ResolveAsync (null, default); - ImmutableInterlocked.InterlockedInitialize (ref s_netcoreappRefs, refs); - } - return s_netcoreappRefs; - } - public static string FindTestSuiteDir (string rootDir, string suiteName) { string[] suiteParts = suiteName.Split ('.'); @@ -82,7 +64,7 @@ public static async Task RunTestFile (string suiteName, string testName, bool al .Select (f => SyntaxFactory.ParseSyntaxTree (SourceText.From (File.OpenRead (f)))); var additionalFiles = GetAdditionalFiles (rootSourceDir, tree); - var (comp, model, exceptionDiagnostics) = await TestCaseCompilation.CreateCompilation ( + var (comp, model, exceptionDiagnostics) = TestCaseCompilation.CreateCompilation ( tree, consoleApplication: false, msbuildProperties, @@ -166,10 +148,7 @@ private static IEnumerable GetAdditionalFiles (string rootSource public static void GetDirectoryPaths (out string rootSourceDirectory) { - var artifactsBinDirectory = (string)AppContext.GetData("ILLink.RoslynAnalyzer.Tests.ArtifactsBinDir")!; - var linkerTestDirectory = (string)AppContext.GetData("ILLink.RoslynAnalyzer.Tests.LinkerTestDir")!; - var configuration = (string)AppContext.GetData("ILLink.RoslynAnalyzer.Tests.Configuration")!; - + string linkerTestDirectory = (string)AppContext.GetData("ILLink.RoslynAnalyzer.Tests.LinkerTestDir")!; rootSourceDirectory = Path.GetFullPath(Path.Combine(linkerTestDirectory, MonoLinkerTestsCases)); } @@ -234,10 +213,5 @@ public static (string, string)[] UseMSBuildProperties (params string[] MSBuildPr { return MSBuildProperties.Select (msbp => ($"build_property.{msbp}", "true")).ToArray (); } - - public static string GetRepoRoot () - { - return (string)AppContext.GetData("ILLink.RoslynAnalyzer.Tests.RepoRoot")!; - } } } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs index 2d2dbb8035c409..7210e262beb07c 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs @@ -42,8 +42,7 @@ static Task VerifyUnconditionalSuppressMessageCodeFixWithRUC ( { var test = new VerifyCSUSM.Test { TestCode = source, - FixedCode = fixedSource, - ReferenceAssemblies = TestCaseUtils.NetCoreAppReferencessemblies + FixedCode = fixedSource }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( @@ -62,8 +61,7 @@ static Task VerifyUnconditionalSuppressMessageCodeFixWithRAF ( { var test = new VerifyCSUSM.Test { TestCode = source, - FixedCode = fixedSource, - ReferenceAssemblies = TestCaseUtils.NetCoreAppReferencessemblies + FixedCode = fixedSource }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( @@ -82,8 +80,7 @@ static Task VerifyUnconditionalSuppressMessageCodeFixWithRDC ( { var test = new VerifyCSUSM.Test { TestCode = source + dynamicCodeAttribute, - FixedCode = fixedSource + dynamicCodeAttribute, - ReferenceAssemblies = TestCaseUtils.NetCoreAppReferencessemblies + FixedCode = fixedSource + dynamicCodeAttribute }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs index 3256e8da7fbd56..3f8aa28a86996c 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs @@ -46,7 +46,7 @@ public static async Task VerifyAnalyzerAsync ( IEnumerable? additionalReferences = null, params DiagnosticResult[] expected) { - var (comp, _, exceptionDiagnostics) = await TestCaseCompilation.CreateCompilation (src, consoleApplication, analyzerOptions, additionalReferences); + var (comp, _, exceptionDiagnostics) = TestCaseCompilation.CreateCompilation (src, consoleApplication, analyzerOptions, additionalReferences); var diags = (await comp.GetAllDiagnosticsAsync ()).AddRange (exceptionDiagnostics); var analyzers = ImmutableArray.Create (new TAnalyzer ()); VerifyDiagnosticResults (diags, analyzers, expected, DefaultVerifier); diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs index ac59612df8b36f..b541b06db52f6e 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs @@ -29,6 +29,11 @@ public class Test : CSharpCodeFixTest { public Test () { + // Clear out the default reference assemblies. We explicitly add references from the live ref pack, + // so we don't want the Roslyn test infrastructure to resolve/add any default reference assemblies + ReferenceAssemblies = new ReferenceAssemblies(string.Empty); + TestState.AdditionalReferences.AddRange(SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences()); + SolutionTransforms.Add ((solution, projectId) => { var compilationOptions = solution.GetProject (projectId)!.CompilationOptions; compilationOptions = compilationOptions!.WithSpecificDiagnosticOptions ( From e3519f9f47696ecaf2b1abdd0b48a055dce6708c Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 29 Dec 2023 12:35:37 +0100 Subject: [PATCH 041/189] Fix ILCompiler tests --- .../ILCompiler.Trimming.Tests.csproj | 11 ++++++----- .../TestCasesRunner/TestCaseSandbox.cs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj index a8d2f877e69aa9..b8455700bc57d9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj @@ -16,16 +16,17 @@ - - + + + $(RuntimeBinDir) @@ -35,15 +36,15 @@ $(Configuration) - - $(ArtifactsDir) - $(CoreCLRArtifactsPath) $(ArtifactsBinDir) + + $(TargetFramework) + $(ToolsProjectRoot)illink/test/ diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TestCaseSandbox.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TestCaseSandbox.cs index 2b70191db53078..c3cd3fbcd3b3ae 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TestCaseSandbox.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TestCaseSandbox.cs @@ -18,7 +18,7 @@ private static partial NPath GetArtifactsTestPath () { // Converts paths like /root-folder/runtime/artifacts/bin/Mono.Linker.Tests/x64/Debug/Mono.Linker.Tests.dll // to /root-folder/runtime/artifacts/bin/ILLink.testcases/ - string artifacts = (string) AppContext.GetData ("Mono.Linker.Tests.ArtifactsDir")!; + string artifacts = (string) AppContext.GetData ("Mono.Linker.Tests.ArtifactsBinDir")!; string tests = Path.Combine (artifacts, "ILLink.testcases"); return new NPath (tests); } From 4d691f0d9b3465f0a3848b4931258e1fd7bed0b5 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 3 Jan 2024 19:54:58 +0000 Subject: [PATCH 042/189] [wasi] Always add wasi-wasm rid to KnownRuntimePack for local builds in targetingpacks.targets --- eng/targetingpacks.targets | 9 +++++++++ src/mono/wasi/build/WasiApp.InTree.targets | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets index 162b6c5210ad06..ab4c6c3e72852c 100644 --- a/eng/targetingpacks.targets +++ b/eng/targetingpacks.targets @@ -188,4 +188,13 @@ DotNetHostPath="$(DotNetTool)" /> + + + + + + + diff --git a/src/mono/wasi/build/WasiApp.InTree.targets b/src/mono/wasi/build/WasiApp.InTree.targets index 39236ac77c4f17..5a48423781d861 100644 --- a/src/mono/wasi/build/WasiApp.InTree.targets +++ b/src/mono/wasi/build/WasiApp.InTree.targets @@ -20,14 +20,6 @@ - - - - - - - - From 275ead5e810e68b9e767e7cde636a4df768286bc Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 3 Jan 2024 20:55:17 +0000 Subject: [PATCH 043/189] wip --- eng/testing/tests.browser.targets | 88 ----------------- eng/testing/tests.wasi.targets | 17 ---- eng/testing/tests.wasm.targets | 26 ----- eng/testing/workloads-browser.targets | 96 +++++++++++++++++++ eng/testing/workloads-testing.targets | 11 ++- eng/testing/workloads-wasi.targets | 24 +++++ eng/testing/workloads-wasm.targets | 28 ++++++ eng/testing/workoads-wasi.targets | 28 ++++++ src/libraries/Directory.Build.props | 7 +- src/mono/wasi/Makefile | 2 +- .../Wasi.Build.Tests/Directory.Build.props | 9 ++ .../Wasi.Build.Tests/Wasi.Build.Tests.csproj | 3 +- 12 files changed, 200 insertions(+), 139 deletions(-) create mode 100644 eng/testing/workloads-browser.targets create mode 100644 eng/testing/workloads-wasi.targets create mode 100644 eng/testing/workloads-wasm.targets create mode 100644 eng/testing/workoads-wasi.targets diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index ea561e42ccaff4..005a43083a823f 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -23,7 +23,6 @@ true false - _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) true - _GetRuntimePackNuGetsToBuild;_GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasmBuildApp;_PrepareForAOTOnHelix true false @@ -272,90 +270,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - <_DefaultBuildVariant Condition="'$(MonoWasmBuildVariant)' == 'multithread'">.multithread. - <_DefaultBuildVariant Condition="'$(_DefaultBuildVariant)' == ''">. - - <_DefaultRuntimePackNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono$(_DefaultBuildVariant)$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg - - - - <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RuntimeIdentifier).*$(PackageVersionForWorkloadManifests).nupkg" /> - <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.*.$(RuntimeIdentifier).*$(PackageVersionForWorkloadManifests).nupkg" /> - <_RuntimePackNugetAvailable Remove="@(_RuntimePackNugetAvailable)" Condition="$([System.String]::new('%(_RuntimePackNugetAvailable.FileName)').EndsWith('.symbols'))" /> - - - - - - <_BuildVariants Include="multithread" Condition="'$(_DefaultBuildVariant)' != '.multithread.'" /> - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.%(_BuildVariants.Identity).$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" - Dependencies="$(_DefaultRuntimePackNuGetPath)" - Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=%(_BuildVariants.Identity)" - Descriptor="runtime pack for %(_BuildVariants.Identity)" - Condition="'%(_BuildVariants.Identity)' != ''"/> - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=" - Dependencies="$(_DefaultRuntimePackNuGetPath)" - Descriptor="single threaded runtime pack" - Condition="'$(_DefaultBuildVariant)' != '.'" /> - - - - diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 9f4068a1187a94..8516230d7e6da6 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -13,8 +13,6 @@ >true - _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) - _GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasi', 'wasi-sdk')) <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasiBuildApp;_PrepareForAOTOnHelix @@ -158,19 +156,4 @@ - - - - - - - - - diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index e76f1acb374cde..bf29d930d02f59 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -186,30 +186,4 @@ - - - - - <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg - - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';')" - Descriptor="Ref pack"/> - - - <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> - <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> - <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> - - <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" - Properties="@(_PropsForAOTCrossBuild,';')" - Descriptor="AOT Cross compiler"/> - - diff --git a/eng/testing/workloads-browser.targets b/eng/testing/workloads-browser.targets new file mode 100644 index 00000000000000..270267385bab11 --- /dev/null +++ b/eng/testing/workloads-browser.targets @@ -0,0 +1,96 @@ + + + + + _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) + _GetRuntimePackNuGetsToBuild;_GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) + + + + + + + + + + + + + + + + + + + + + + + + + + + <_DefaultBuildVariant Condition="'$(MonoWasmBuildVariant)' == 'multithread'">.multithread. + <_DefaultBuildVariant Condition="'$(_DefaultBuildVariant)' == ''">. + + <_DefaultRuntimePackNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono$(_DefaultBuildVariant)$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg + + + + <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RIDForWorkload).*$(PackageVersionForWorkloadManifests).nupkg" /> + <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.*.$(RIDForWorkload).*$(PackageVersionForWorkloadManifests).nupkg" /> + <_RuntimePackNugetAvailable Remove="@(_RuntimePackNugetAvailable)" Condition="$([System.String]::new('%(_RuntimePackNugetAvailable.FileName)').EndsWith('.symbols'))" /> + + + + + + <_BuildVariants Include="multithread" Condition="'$(_DefaultBuildVariant)' != '.multithread.'" /> + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.%(_BuildVariants.Identity).$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" + Dependencies="$(_DefaultRuntimePackNuGetPath)" + Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=%(_BuildVariants.Identity)" + Descriptor="runtime pack for %(_BuildVariants.Identity)" + Condition="'%(_BuildVariants.Identity)' != ''"/> + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=" + Dependencies="$(_DefaultRuntimePackNuGetPath)" + Descriptor="single threaded runtime pack" + Condition="'$(_DefaultBuildVariant)' != '.'" /> + + + + + diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets index 5c4e021e7a8849..636f65052eedd1 100644 --- a/eng/testing/workloads-testing.targets +++ b/eng/testing/workloads-testing.targets @@ -55,8 +55,8 @@ <_DefaultPropsForNuGetBuild Include="Configuration=$(Configuration)" /> - <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOS)" /> - <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitecture)" /> + <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOSForWorkload)" /> + <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitectureForWorkload)" /> <_DefaultPropsForNuGetBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> @@ -150,6 +150,8 @@ + + @@ -157,7 +159,7 @@ <_SdkWithWorkloadToInstall Include="@(WorkloadCombinationsToInstall)" /> <_SdkWithWorkloadToInstall InstallPath="$(_SdkForWorkloadTestingBasePath)\dotnet-%(Identity)" /> - <_SdkWithWorkloadToInstall StampPath="%(InstallPath)\.workload-installed.$(RuntimeIdentifier).stamp" /> + <_SdkWithWorkloadToInstall StampPath="%(InstallPath)\.workload-installed.$(RIDForWorkload).stamp" /> @@ -216,4 +218,7 @@ + + + diff --git a/eng/testing/workloads-wasi.targets b/eng/testing/workloads-wasi.targets new file mode 100644 index 00000000000000..356cad0fa06eff --- /dev/null +++ b/eng/testing/workloads-wasi.targets @@ -0,0 +1,24 @@ + + + + + _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) + _GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) + + + + + + + + + + + + diff --git a/eng/testing/workloads-wasm.targets b/eng/testing/workloads-wasm.targets new file mode 100644 index 00000000000000..eebcdfcf45604e --- /dev/null +++ b/eng/testing/workloads-wasm.targets @@ -0,0 +1,28 @@ + + + + + + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg + + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';')" + Descriptor="Ref pack"/> + + + <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> + <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> + <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RIDForWorkload)" /> + <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> + + <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" + Properties="@(_PropsForAOTCrossBuild,';')" + Descriptor="AOT Cross compiler"/> + + + diff --git a/eng/testing/workoads-wasi.targets b/eng/testing/workoads-wasi.targets new file mode 100644 index 00000000000000..8b335fbcca1e24 --- /dev/null +++ b/eng/testing/workoads-wasi.targets @@ -0,0 +1,28 @@ + + + + + + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg + + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';')" + Descriptor="Ref pack"/> + + + <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> + <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> + <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> + + <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" + Properties="@(_PropsForAOTCrossBuild,';')" + Descriptor="AOT Cross compiler"/> + + + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index af6fc30ae1ba87..b37bae89cb86cc 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -94,15 +94,16 @@ --interpreter - + $(ArtifactsBinDir)dotnet-none\ $([MSBuild]::NormalizeDirectory($(SdkWithNoWorkloadForTestingPath))) $(SdkWithNoWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp $(SdkWithNoWorkloadForTestingPath)workload.stamp - $(ArtifactsBinDir)dotnet-latest\ - $(ArtifactsBinDir)dotnet-latest\ + $(ArtifactsBinDir)dotnet-latest\ + + $([MSBuild]::NormalizeDirectory($(SdkWithWorkloadForTestingPath))) $(SdkWithWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp diff --git a/src/mono/wasi/Makefile b/src/mono/wasi/Makefile index 7fc6904bc0702f..3554f20f99a202 100644 --- a/src/mono/wasi/Makefile +++ b/src/mono/wasi/Makefile @@ -65,7 +65,7 @@ run-tests-%: $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) run-build-tests: - WASI_SDK_PATH=$(WASI_SDK_PATH) $(DOTNET) build $(TOP)/src/mono/wasi//Wasi.Build.Tests/ /t:Test /bl $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) + WASI_SDK_PATH=$(WASI_SDK_PATH) $(DOTNET) build $(TOP)/src/mono/wasi//Wasi.Build.Tests/ /t:Test /bl /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) build-debugger-tests-helix: $(DOTNET) build -restore -bl:$(TOP)/artifacts/log/$(CONFIG)/Wasm.Debugger.Tests.binlog \ diff --git a/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props b/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props index 102dd7c86919d9..b890588c22160c 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props +++ b/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props @@ -4,7 +4,16 @@ BuildWasmApps true Wasi.Build.Tests + + wasi-wasm + wasi + wasm + + $(OutputRID) + true + true + diff --git a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj index cc27e159d25e6e..19c41e2b440015 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj +++ b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj @@ -1,6 +1,7 @@ $(NetCoreAppToolCurrent) + true true true @@ -103,7 +104,7 @@ - dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml + /workspaces/runtime/.dotnet/dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml $(RunScriptCommand) %24HELIX_XUNIT_ARGS From 01cf82aa8e3ff0e8054a03bfcfb66747927038db Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 3 Jan 2024 21:11:19 +0000 Subject: [PATCH 044/189] disable unrelated builds --- eng/pipelines/common/platform-matrix.yml | 871 ------------------ .../Wasi.Build.Tests/Wasi.Build.Tests.csproj | 2 +- 2 files changed, 1 insertion(+), 872 deletions(-) diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 776cdae314c429..229e2e7d72a146 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -26,463 +26,6 @@ jobs: # Linux arm -- ${{ if or(containsValue(parameters.platforms, 'linux_arm'), in(parameters.platformGroup, 'all', 'gcstress')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: arm - targetRid: linux-arm - platform: linux_arm - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_arm - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux armv6 - -- ${{ if containsValue(parameters.platforms, 'linux_armv6') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: armv6 - targetRid: linux-armv6 - platform: linux_armv6 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_armv6 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux arm64 - -- ${{ if or(containsValue(parameters.platforms, 'linux_arm64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: arm64 - targetRid: linux-arm64 - platform: linux_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - ${{ if eq(parameters.container, '') }}: - container: linux_arm64 - ${{ if ne(parameters.container, '') }}: - container: - image: ${{ parameters.container }} - registry: mcr - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux musl x64 - -- ${{ if or(containsValue(parameters.platforms, 'linux_musl_x64'), eq(parameters.platformGroup, 'all')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - osSubgroup: _musl - archType: x64 - targetRid: linux-musl-x64 - platform: linux_musl_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_musl_x64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux musl arm - -- ${{ if or(containsValue(parameters.platforms, 'linux_musl_arm'), eq(parameters.platformGroup, 'all')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - osSubgroup: _musl - archType: arm - targetRid: linux-musl-arm - platform: linux_musl_arm - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_musl_arm - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux musl arm64 - -- ${{ if or(containsValue(parameters.platforms, 'linux_musl_arm64'), eq(parameters.platformGroup, 'all')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - osSubgroup: _musl - archType: arm64 - targetRid: linux-musl-arm64 - platform: linux_musl_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_musl_arm64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux Bionic arm64 - -- ${{ if containsValue(parameters.platforms, 'linux_bionic_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - osSubgroup: _bionic - archType: arm64 - targetRid: linux-bionic-arm64 - platform: linux_bionic_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic - jobParameters: - runtimeFlavor: mono - # We build on Linux, but the test queue runs Windows, so - # we need to override the test script generation - runScriptWindowsCmd: true - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux Bionic x64 - -- ${{ if containsValue(parameters.platforms, 'linux_bionic_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - osSubgroup: _bionic - archType: x64 - targetRid: linux-bionic-x64 - platform: linux_bionic_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic - jobParameters: - runtimeFlavor: mono - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux x64 - -- ${{ if or(containsValue(parameters.platforms, 'linux_x64'), containsValue(parameters.platforms, 'CoreClrTestBuildHost'), in(parameters.platformGroup, 'all', 'gcstress')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: linux-x64 - platform: linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - ${{ if eq(parameters.container, '') }}: - container: linux_x64 - ${{ if ne(parameters.container, '') }}: - container: - image: ${{ parameters.container }} - registry: mcr - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux x86 - -- ${{ if containsValue(parameters.platforms, 'linux_x86') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x86 - targetRid: linux-x86 - platform: linux_x86 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_x86 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - disableClrTest: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Runtime-dev-innerloop build - -- ${{ if containsValue(parameters.platforms, 'linux_x64_dev_innerloop') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: linux-x64 - platform: linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_x64_dev_innerloop - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Centos 8 Stream x64 Source Build - -- ${{ if containsValue(parameters.platforms, 'SourceBuild_centos8_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: centos.8-x64 - platform: centos8_linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: SourceBuild_centos_x64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - isSourceBuild: true - isNonPortableSourceBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Non-existent RID Source Build - -- ${{ if containsValue(parameters.platforms, 'SourceBuild_banana24_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: banana.24-x64 - platform: banana24_linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: SourceBuild_centos_x64 # Run the unknown-rid build on a platform with a known RID so our RID graph tooling can automatically add it to the RID graph. - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - baseOS: linux - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - isSourceBuild: true - isNonPortableSourceBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Portable Linux x64 Source Build - -- ${{ if containsValue(parameters.platforms, 'SourceBuild_linux_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: linux-x64 - platform: linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: SourceBuild_linux_x64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - isSourceBuild: true - isNonPortableSourceBuild: false - ${{ insert }}: ${{ parameters.jobParameters }} - -# GCC Linux x64 Build - -- ${{ if containsValue(parameters.platforms, 'gcc_linux_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: linux-x64 - platform: linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: debian-12-gcc13-amd64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Mono LLVMAot test build - -- ${{ if containsValue(parameters.platforms, 'linux_x64_llvmaot') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: x64 - targetRid: linux-x64 - platform: linux_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_x64_llvmaot - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux s390x - -- ${{ if containsValue(parameters.platforms, 'linux_s390x') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: s390x - targetRid: linux-s390x - platform: linux_s390x - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_s390x - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux PPC64le - -- ${{ if containsValue(parameters.platforms, 'linux_ppc64le') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: ppc64le - targetRid: linux-ppc64le - platform: linux_ppc64le - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_ppc64le - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Linux RISCV64 - -- ${{ if containsValue(parameters.platforms, 'linux_riscv64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux - archType: riscv64 - targetRid: linux-riscv64 - platform: linux_riscv64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_riscv64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - disableClrTest: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# WASI WebAssembly - -- ${{ if containsValue(parameters.platforms, 'wasi_wasm') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: wasi - archType: wasm - targetRid: wasi-wasm - platform: wasi_wasm - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: wasi_wasm - jobParameters: - hostedOs: linux - runtimeFlavor: ${{ parameters.runtimeFlavor }} - stagedBuild: ${{ parameters.stagedBuild }} - buildConfig: ${{ parameters.buildConfig }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# WASI WebAssembly windows - -- ${{ if containsValue(parameters.platforms, 'wasi_wasm_win') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: wasi - archType: wasm - targetRid: wasi-wasm - platform: wasi_wasm_win - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - hostedOs: windows - runtimeFlavor: ${{ parameters.runtimeFlavor }} - stagedBuild: ${{ parameters.stagedBuild }} - buildConfig: ${{ parameters.buildConfig }} - ${{ insert }}: ${{ parameters.jobParameters }} - # Browser WebAssembly - ${{ if containsValue(parameters.platforms, 'browser_wasm') }}: @@ -541,417 +84,3 @@ jobs: runtimeFlavor: ${{ parameters.runtimeFlavor }} buildConfig: ${{ parameters.buildConfig }} ${{ insert }}: ${{ parameters.jobParameters }} - -# FreeBSD -- ${{ if containsValue(parameters.platforms, 'freebsd_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: freebsd - archType: x64 - targetRid: freebsd-x64 - platform: freebsd_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: freebsd_x64 - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Android x64 - -- ${{ if containsValue(parameters.platforms, 'android_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: android - archType: x64 - targetRid: android-x64 - platform: android_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic - jobParameters: - runtimeFlavor: mono - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Android x64 with Docker-in-Docker - -- ${{ if containsValue(parameters.platforms, 'android_x64_docker') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: android - archType: x64 - targetRid: android-x64 - platform: android_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: android_docker - jobParameters: - runtimeFlavor: mono - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Android x86 - -- ${{ if containsValue(parameters.platforms, 'android_x86') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: android - archType: x86 - targetRid: android-x86 - platform: android_x86 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic - jobParameters: - runtimeFlavor: mono - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Android arm - -- ${{ if containsValue(parameters.platforms, 'android_arm') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: android - archType: arm - targetRid: android-arm - platform: android_arm - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic - jobParameters: - runtimeFlavor: mono - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Android arm64 - -- ${{ if containsValue(parameters.platforms, 'android_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: android - archType: arm64 - targetRid: android-arm64 - platform: android_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: linux_bionic - jobParameters: - runtimeFlavor: mono - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Mac Catalyst x64 - -- ${{ if containsValue(parameters.platforms, 'maccatalyst_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: maccatalyst - archType: x64 - targetRid: maccatalyst-x64 - platform: maccatalyst_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Mac Catalyst arm64 - -- ${{ if containsValue(parameters.platforms, 'maccatalyst_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: maccatalyst - archType: arm64 - targetRid: maccatalyst-arm64 - platform: maccatalyst_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# tvOS arm64 - -- ${{ if containsValue(parameters.platforms, 'tvos_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: tvos - archType: arm64 - targetRid: tvos-arm64 - platform: tvos_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# tvOS Simulator x64 - -- ${{ if containsValue(parameters.platforms, 'tvossimulator_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: tvossimulator - archType: x64 - targetRid: tvossimulator-x64 - platform: tvossimulator_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# tvOS Simulator arm64 - -- ${{ if containsValue(parameters.platforms, 'tvossimulator_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: tvossimulator - archType: arm64 - targetRid: tvossimulator-arm64 - platform: tvossimulator_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# iOS arm64 - -- ${{ if containsValue(parameters.platforms, 'ios_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: ios - archType: arm64 - targetRid: ios-arm64 - platform: ios_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# iOS Simulator x64 - -- ${{ if containsValue(parameters.platforms, 'iossimulator_x64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: iossimulator - archType: x64 - targetRid: iossimulator-x64 - platform: iossimulator_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# iOS Simulator arm64 - -- ${{ if containsValue(parameters.platforms, 'iossimulator_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: iossimulator - archType: arm64 - targetRid: iossimulator-arm64 - platform: iossimulator_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - ${{ if eq(parameters.runtimeFlavor, '') }}: - runtimeFlavor: mono - ${{ else }}: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# macOS arm64 - -- ${{ if containsValue(parameters.platforms, 'osx_arm64') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: osx - archType: arm64 - targetRid: osx-arm64 - platform: osx_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# macOS x64 - -- ${{ if or(containsValue(parameters.platforms, 'osx_x64'), eq(parameters.platformGroup, 'all')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: osx - archType: x64 - targetRid: osx-x64 - platform: osx_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Tizen armel - -- ${{ if containsValue(parameters.platforms, 'tizen_armel') }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: linux # Our build scripts don't support Tizen and have always used Linux as the OS parameter. - archType: armel - targetRid: tizen-armel - platform: tizen_armel - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - container: tizen_armel - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - crossBuild: true - disableClrTest: true - ${{ insert }}: ${{ parameters.jobParameters }} - -# Windows x64 - -- ${{ if or(containsValue(parameters.platforms, 'windows_x64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: windows - archType: x64 - targetRid: win-x64 - platform: windows_x64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Windows x86 - -- ${{ if or(containsValue(parameters.platforms, 'windows_x86'), in(parameters.platformGroup, 'all', 'gcstress')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: windows - archType: x86 - targetRid: win-x86 - platform: windows_x86 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} - -# Windows arm64 - -- ${{ if or(containsValue(parameters.platforms, 'windows_arm64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: - - template: xplat-setup.yml - parameters: - jobTemplate: ${{ parameters.jobTemplate }} - helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} - variables: ${{ parameters.variables }} - osGroup: windows - archType: arm64 - targetRid: win-arm64 - platform: windows_arm64 - shouldContinueOnError: ${{ parameters.shouldContinueOnError }} - jobParameters: - runtimeFlavor: ${{ parameters.runtimeFlavor }} - buildConfig: ${{ parameters.buildConfig }} - helixQueueGroup: ${{ parameters.helixQueueGroup }} - ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj index 19c41e2b440015..722c188bafa5bb 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj +++ b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj @@ -104,7 +104,7 @@ - /workspaces/runtime/.dotnet/dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml + dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml $(RunScriptCommand) %24HELIX_XUNIT_ARGS From ebbc65763af9f8f1b01489afa520728db1db7e8b Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 15:36:35 -0500 Subject: [PATCH 045/189] split ci --- .../templates/simple-wasm-build-tests.yml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/eng/pipelines/common/templates/simple-wasm-build-tests.yml b/eng/pipelines/common/templates/simple-wasm-build-tests.yml index 7a593f4f9be203..34306fe09aa88e 100644 --- a/eng/pipelines/common/templates/simple-wasm-build-tests.yml +++ b/eng/pipelines/common/templates/simple-wasm-build-tests.yml @@ -30,6 +30,17 @@ jobs: eq(variables['Build.SourceBranchName'], 'main'), eq(variables['System.PullRequest.TargetBranch'], 'main')) ] + - name: wbtProjectArg + ${{ if eq(platform, 'browser_wasm_win') }}: + value: '-projects' + ${{ else }}: + value: '--projects' + - name: wbtProjectPath + ${{ if contains(platform, 'browser_wasm') }}: + value: 'src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj' + ${{ else }}: + value: 'src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj' + jobParameters: isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }} testGroup: innerloop @@ -42,6 +53,17 @@ jobs: eq(variables['isDefaultPipeline'], variables['shouldRunWasmBuildTestsOnDefaultPipeline'])) # extra steps, run tests postBuildSteps: + - script: >- + $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci + $(wbtProjectArg) $(Build.SourcesDirectory)/$(wbtProjectPath) + /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/WBTBuild.binlog + /p:Configuration=$(_BuildConfig) + /p:TestUsingWorkloads=true + /p:InstallWorkloadForTesting=true + /p:BrowserHost=$(_hostedOs) + /p:WorkloadsTestPreviousVersions=$(workloadsTestPreviousVersionsVar) + /p:ArchiveTests=true + - template: /eng/pipelines/libraries/helix.yml parameters: creator: dotnet-bot From 1904bc47f89ac75b2cf82168528f1be9fd3bf21b Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 15:43:56 -0500 Subject: [PATCH 046/189] Revert "split ci" This reverts commit ebbc65763af9f8f1b01489afa520728db1db7e8b. --- .../templates/simple-wasm-build-tests.yml | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/eng/pipelines/common/templates/simple-wasm-build-tests.yml b/eng/pipelines/common/templates/simple-wasm-build-tests.yml index 34306fe09aa88e..7a593f4f9be203 100644 --- a/eng/pipelines/common/templates/simple-wasm-build-tests.yml +++ b/eng/pipelines/common/templates/simple-wasm-build-tests.yml @@ -30,17 +30,6 @@ jobs: eq(variables['Build.SourceBranchName'], 'main'), eq(variables['System.PullRequest.TargetBranch'], 'main')) ] - - name: wbtProjectArg - ${{ if eq(platform, 'browser_wasm_win') }}: - value: '-projects' - ${{ else }}: - value: '--projects' - - name: wbtProjectPath - ${{ if contains(platform, 'browser_wasm') }}: - value: 'src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj' - ${{ else }}: - value: 'src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj' - jobParameters: isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }} testGroup: innerloop @@ -53,17 +42,6 @@ jobs: eq(variables['isDefaultPipeline'], variables['shouldRunWasmBuildTestsOnDefaultPipeline'])) # extra steps, run tests postBuildSteps: - - script: >- - $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci - $(wbtProjectArg) $(Build.SourcesDirectory)/$(wbtProjectPath) - /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/WBTBuild.binlog - /p:Configuration=$(_BuildConfig) - /p:TestUsingWorkloads=true - /p:InstallWorkloadForTesting=true - /p:BrowserHost=$(_hostedOs) - /p:WorkloadsTestPreviousVersions=$(workloadsTestPreviousVersionsVar) - /p:ArchiveTests=true - - template: /eng/pipelines/libraries/helix.yml parameters: creator: dotnet-bot From 0657a86b1fab7e30a6a0053556b8c0ebb3ba11f1 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 15:45:04 -0500 Subject: [PATCH 047/189] Revert "disable unrelated builds" This reverts commit 01cf82aa8e3ff0e8054a03bfcfb66747927038db. --- eng/pipelines/common/platform-matrix.yml | 871 ++++++++++++++++++ .../Wasi.Build.Tests/Wasi.Build.Tests.csproj | 2 +- 2 files changed, 872 insertions(+), 1 deletion(-) diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 229e2e7d72a146..776cdae314c429 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -26,6 +26,463 @@ jobs: # Linux arm +- ${{ if or(containsValue(parameters.platforms, 'linux_arm'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: arm + targetRid: linux-arm + platform: linux_arm + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_arm + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux armv6 + +- ${{ if containsValue(parameters.platforms, 'linux_armv6') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: armv6 + targetRid: linux-armv6 + platform: linux_armv6 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_armv6 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux arm64 + +- ${{ if or(containsValue(parameters.platforms, 'linux_arm64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: arm64 + targetRid: linux-arm64 + platform: linux_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + ${{ if eq(parameters.container, '') }}: + container: linux_arm64 + ${{ if ne(parameters.container, '') }}: + container: + image: ${{ parameters.container }} + registry: mcr + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux musl x64 + +- ${{ if or(containsValue(parameters.platforms, 'linux_musl_x64'), eq(parameters.platformGroup, 'all')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + osSubgroup: _musl + archType: x64 + targetRid: linux-musl-x64 + platform: linux_musl_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_musl_x64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux musl arm + +- ${{ if or(containsValue(parameters.platforms, 'linux_musl_arm'), eq(parameters.platformGroup, 'all')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + osSubgroup: _musl + archType: arm + targetRid: linux-musl-arm + platform: linux_musl_arm + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_musl_arm + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux musl arm64 + +- ${{ if or(containsValue(parameters.platforms, 'linux_musl_arm64'), eq(parameters.platformGroup, 'all')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + osSubgroup: _musl + archType: arm64 + targetRid: linux-musl-arm64 + platform: linux_musl_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_musl_arm64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux Bionic arm64 + +- ${{ if containsValue(parameters.platforms, 'linux_bionic_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + osSubgroup: _bionic + archType: arm64 + targetRid: linux-bionic-arm64 + platform: linux_bionic_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_bionic + jobParameters: + runtimeFlavor: mono + # We build on Linux, but the test queue runs Windows, so + # we need to override the test script generation + runScriptWindowsCmd: true + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux Bionic x64 + +- ${{ if containsValue(parameters.platforms, 'linux_bionic_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + osSubgroup: _bionic + archType: x64 + targetRid: linux-bionic-x64 + platform: linux_bionic_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_bionic + jobParameters: + runtimeFlavor: mono + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux x64 + +- ${{ if or(containsValue(parameters.platforms, 'linux_x64'), containsValue(parameters.platforms, 'CoreClrTestBuildHost'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: linux-x64 + platform: linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + ${{ if eq(parameters.container, '') }}: + container: linux_x64 + ${{ if ne(parameters.container, '') }}: + container: + image: ${{ parameters.container }} + registry: mcr + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux x86 + +- ${{ if containsValue(parameters.platforms, 'linux_x86') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x86 + targetRid: linux-x86 + platform: linux_x86 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_x86 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + disableClrTest: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Runtime-dev-innerloop build + +- ${{ if containsValue(parameters.platforms, 'linux_x64_dev_innerloop') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: linux-x64 + platform: linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_x64_dev_innerloop + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Centos 8 Stream x64 Source Build + +- ${{ if containsValue(parameters.platforms, 'SourceBuild_centos8_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: centos.8-x64 + platform: centos8_linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: SourceBuild_centos_x64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + isSourceBuild: true + isNonPortableSourceBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Non-existent RID Source Build + +- ${{ if containsValue(parameters.platforms, 'SourceBuild_banana24_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: banana.24-x64 + platform: banana24_linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: SourceBuild_centos_x64 # Run the unknown-rid build on a platform with a known RID so our RID graph tooling can automatically add it to the RID graph. + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + baseOS: linux + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + isSourceBuild: true + isNonPortableSourceBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Portable Linux x64 Source Build + +- ${{ if containsValue(parameters.platforms, 'SourceBuild_linux_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: linux-x64 + platform: linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: SourceBuild_linux_x64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + isSourceBuild: true + isNonPortableSourceBuild: false + ${{ insert }}: ${{ parameters.jobParameters }} + +# GCC Linux x64 Build + +- ${{ if containsValue(parameters.platforms, 'gcc_linux_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: linux-x64 + platform: linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: debian-12-gcc13-amd64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Mono LLVMAot test build + +- ${{ if containsValue(parameters.platforms, 'linux_x64_llvmaot') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: x64 + targetRid: linux-x64 + platform: linux_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_x64_llvmaot + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux s390x + +- ${{ if containsValue(parameters.platforms, 'linux_s390x') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: s390x + targetRid: linux-s390x + platform: linux_s390x + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_s390x + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux PPC64le + +- ${{ if containsValue(parameters.platforms, 'linux_ppc64le') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: ppc64le + targetRid: linux-ppc64le + platform: linux_ppc64le + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_ppc64le + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Linux RISCV64 + +- ${{ if containsValue(parameters.platforms, 'linux_riscv64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux + archType: riscv64 + targetRid: linux-riscv64 + platform: linux_riscv64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_riscv64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + disableClrTest: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# WASI WebAssembly + +- ${{ if containsValue(parameters.platforms, 'wasi_wasm') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: wasi + archType: wasm + targetRid: wasi-wasm + platform: wasi_wasm + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: wasi_wasm + jobParameters: + hostedOs: linux + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# WASI WebAssembly windows + +- ${{ if containsValue(parameters.platforms, 'wasi_wasm_win') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: wasi + archType: wasm + targetRid: wasi-wasm + platform: wasi_wasm_win + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + hostedOs: windows + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ insert }}: ${{ parameters.jobParameters }} + # Browser WebAssembly - ${{ if containsValue(parameters.platforms, 'browser_wasm') }}: @@ -84,3 +541,417 @@ jobs: runtimeFlavor: ${{ parameters.runtimeFlavor }} buildConfig: ${{ parameters.buildConfig }} ${{ insert }}: ${{ parameters.jobParameters }} + +# FreeBSD +- ${{ if containsValue(parameters.platforms, 'freebsd_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: freebsd + archType: x64 + targetRid: freebsd-x64 + platform: freebsd_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: freebsd_x64 + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Android x64 + +- ${{ if containsValue(parameters.platforms, 'android_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: android + archType: x64 + targetRid: android-x64 + platform: android_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_bionic + jobParameters: + runtimeFlavor: mono + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Android x64 with Docker-in-Docker + +- ${{ if containsValue(parameters.platforms, 'android_x64_docker') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: android + archType: x64 + targetRid: android-x64 + platform: android_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: android_docker + jobParameters: + runtimeFlavor: mono + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Android x86 + +- ${{ if containsValue(parameters.platforms, 'android_x86') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: android + archType: x86 + targetRid: android-x86 + platform: android_x86 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_bionic + jobParameters: + runtimeFlavor: mono + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Android arm + +- ${{ if containsValue(parameters.platforms, 'android_arm') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: android + archType: arm + targetRid: android-arm + platform: android_arm + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_bionic + jobParameters: + runtimeFlavor: mono + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Android arm64 + +- ${{ if containsValue(parameters.platforms, 'android_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: android + archType: arm64 + targetRid: android-arm64 + platform: android_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: linux_bionic + jobParameters: + runtimeFlavor: mono + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Mac Catalyst x64 + +- ${{ if containsValue(parameters.platforms, 'maccatalyst_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: maccatalyst + archType: x64 + targetRid: maccatalyst-x64 + platform: maccatalyst_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Mac Catalyst arm64 + +- ${{ if containsValue(parameters.platforms, 'maccatalyst_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: maccatalyst + archType: arm64 + targetRid: maccatalyst-arm64 + platform: maccatalyst_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# tvOS arm64 + +- ${{ if containsValue(parameters.platforms, 'tvos_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: tvos + archType: arm64 + targetRid: tvos-arm64 + platform: tvos_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# tvOS Simulator x64 + +- ${{ if containsValue(parameters.platforms, 'tvossimulator_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: tvossimulator + archType: x64 + targetRid: tvossimulator-x64 + platform: tvossimulator_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# tvOS Simulator arm64 + +- ${{ if containsValue(parameters.platforms, 'tvossimulator_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: tvossimulator + archType: arm64 + targetRid: tvossimulator-arm64 + platform: tvossimulator_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# iOS arm64 + +- ${{ if containsValue(parameters.platforms, 'ios_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: ios + archType: arm64 + targetRid: ios-arm64 + platform: ios_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# iOS Simulator x64 + +- ${{ if containsValue(parameters.platforms, 'iossimulator_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: iossimulator + archType: x64 + targetRid: iossimulator-x64 + platform: iossimulator_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# iOS Simulator arm64 + +- ${{ if containsValue(parameters.platforms, 'iossimulator_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: iossimulator + archType: arm64 + targetRid: iossimulator-arm64 + platform: iossimulator_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + ${{ if eq(parameters.runtimeFlavor, '') }}: + runtimeFlavor: mono + ${{ else }}: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# macOS arm64 + +- ${{ if containsValue(parameters.platforms, 'osx_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: osx + archType: arm64 + targetRid: osx-arm64 + platform: osx_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# macOS x64 + +- ${{ if or(containsValue(parameters.platforms, 'osx_x64'), eq(parameters.platformGroup, 'all')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: osx + archType: x64 + targetRid: osx-x64 + platform: osx_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Tizen armel + +- ${{ if containsValue(parameters.platforms, 'tizen_armel') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: linux # Our build scripts don't support Tizen and have always used Linux as the OS parameter. + archType: armel + targetRid: tizen-armel + platform: tizen_armel + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: tizen_armel + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + crossBuild: true + disableClrTest: true + ${{ insert }}: ${{ parameters.jobParameters }} + +# Windows x64 + +- ${{ if or(containsValue(parameters.platforms, 'windows_x64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: windows + archType: x64 + targetRid: win-x64 + platform: windows_x64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Windows x86 + +- ${{ if or(containsValue(parameters.platforms, 'windows_x86'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: windows + archType: x86 + targetRid: win-x86 + platform: windows_x86 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# Windows arm64 + +- ${{ if or(containsValue(parameters.platforms, 'windows_arm64'), in(parameters.platformGroup, 'all', 'gcstress')) }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: windows + archType: arm64 + targetRid: win-arm64 + platform: windows_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + buildConfig: ${{ parameters.buildConfig }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj index 722c188bafa5bb..19c41e2b440015 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj +++ b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj @@ -104,7 +104,7 @@ - dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml + /workspaces/runtime/.dotnet/dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml $(RunScriptCommand) %24HELIX_XUNIT_ARGS From 88889cc62d92a6696b406376420eecf0c51375de Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 15:45:13 -0500 Subject: [PATCH 048/189] Revert "wip" This reverts commit 275ead5e810e68b9e767e7cde636a4df768286bc. --- eng/testing/tests.browser.targets | 88 +++++++++++++++++ eng/testing/tests.wasi.targets | 17 ++++ eng/testing/tests.wasm.targets | 26 +++++ eng/testing/workloads-browser.targets | 96 ------------------- eng/testing/workloads-testing.targets | 11 +-- eng/testing/workloads-wasi.targets | 24 ----- eng/testing/workloads-wasm.targets | 28 ------ eng/testing/workoads-wasi.targets | 28 ------ src/libraries/Directory.Build.props | 7 +- src/mono/wasi/Makefile | 2 +- .../Wasi.Build.Tests/Directory.Build.props | 9 -- .../Wasi.Build.Tests/Wasi.Build.Tests.csproj | 3 +- 12 files changed, 139 insertions(+), 200 deletions(-) delete mode 100644 eng/testing/workloads-browser.targets delete mode 100644 eng/testing/workloads-wasi.targets delete mode 100644 eng/testing/workloads-wasm.targets delete mode 100644 eng/testing/workoads-wasi.targets diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index 005a43083a823f..ea561e42ccaff4 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -23,6 +23,7 @@ true false + _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) true + _GetRuntimePackNuGetsToBuild;_GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasmBuildApp;_PrepareForAOTOnHelix true false @@ -270,4 +272,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + <_DefaultBuildVariant Condition="'$(MonoWasmBuildVariant)' == 'multithread'">.multithread. + <_DefaultBuildVariant Condition="'$(_DefaultBuildVariant)' == ''">. + + <_DefaultRuntimePackNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono$(_DefaultBuildVariant)$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg + + + + <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RuntimeIdentifier).*$(PackageVersionForWorkloadManifests).nupkg" /> + <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.*.$(RuntimeIdentifier).*$(PackageVersionForWorkloadManifests).nupkg" /> + <_RuntimePackNugetAvailable Remove="@(_RuntimePackNugetAvailable)" Condition="$([System.String]::new('%(_RuntimePackNugetAvailable.FileName)').EndsWith('.symbols'))" /> + + + + + + <_BuildVariants Include="multithread" Condition="'$(_DefaultBuildVariant)' != '.multithread.'" /> + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.%(_BuildVariants.Identity).$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" + Dependencies="$(_DefaultRuntimePackNuGetPath)" + Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=%(_BuildVariants.Identity)" + Descriptor="runtime pack for %(_BuildVariants.Identity)" + Condition="'%(_BuildVariants.Identity)' != ''"/> + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=" + Dependencies="$(_DefaultRuntimePackNuGetPath)" + Descriptor="single threaded runtime pack" + Condition="'$(_DefaultBuildVariant)' != '.'" /> + + + + diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 8516230d7e6da6..9f4068a1187a94 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -13,6 +13,8 @@ >true + _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) + _GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasi', 'wasi-sdk')) <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasiBuildApp;_PrepareForAOTOnHelix @@ -156,4 +158,19 @@ + + + + + + + + + diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index bf29d930d02f59..e76f1acb374cde 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -186,4 +186,30 @@ + + + + + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg + + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';')" + Descriptor="Ref pack"/> + + + <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> + <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> + <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> + + <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" + Properties="@(_PropsForAOTCrossBuild,';')" + Descriptor="AOT Cross compiler"/> + + diff --git a/eng/testing/workloads-browser.targets b/eng/testing/workloads-browser.targets deleted file mode 100644 index 270267385bab11..00000000000000 --- a/eng/testing/workloads-browser.targets +++ /dev/null @@ -1,96 +0,0 @@ - - - - - _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) - _GetRuntimePackNuGetsToBuild;_GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) - - - - - - - - - - - - - - - - - - - - - - - - - - - <_DefaultBuildVariant Condition="'$(MonoWasmBuildVariant)' == 'multithread'">.multithread. - <_DefaultBuildVariant Condition="'$(_DefaultBuildVariant)' == ''">. - - <_DefaultRuntimePackNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono$(_DefaultBuildVariant)$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg - - - - <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RIDForWorkload).*$(PackageVersionForWorkloadManifests).nupkg" /> - <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.*.$(RIDForWorkload).*$(PackageVersionForWorkloadManifests).nupkg" /> - <_RuntimePackNugetAvailable Remove="@(_RuntimePackNugetAvailable)" Condition="$([System.String]::new('%(_RuntimePackNugetAvailable.FileName)').EndsWith('.symbols'))" /> - - - - - - <_BuildVariants Include="multithread" Condition="'$(_DefaultBuildVariant)' != '.multithread.'" /> - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.%(_BuildVariants.Identity).$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" - Dependencies="$(_DefaultRuntimePackNuGetPath)" - Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=%(_BuildVariants.Identity)" - Descriptor="runtime pack for %(_BuildVariants.Identity)" - Condition="'%(_BuildVariants.Identity)' != ''"/> - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=" - Dependencies="$(_DefaultRuntimePackNuGetPath)" - Descriptor="single threaded runtime pack" - Condition="'$(_DefaultBuildVariant)' != '.'" /> - - - - - diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets index 636f65052eedd1..5c4e021e7a8849 100644 --- a/eng/testing/workloads-testing.targets +++ b/eng/testing/workloads-testing.targets @@ -55,8 +55,8 @@ <_DefaultPropsForNuGetBuild Include="Configuration=$(Configuration)" /> - <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOSForWorkload)" /> - <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitectureForWorkload)" /> + <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOS)" /> + <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitecture)" /> <_DefaultPropsForNuGetBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> @@ -150,8 +150,6 @@ - - @@ -159,7 +157,7 @@ <_SdkWithWorkloadToInstall Include="@(WorkloadCombinationsToInstall)" /> <_SdkWithWorkloadToInstall InstallPath="$(_SdkForWorkloadTestingBasePath)\dotnet-%(Identity)" /> - <_SdkWithWorkloadToInstall StampPath="%(InstallPath)\.workload-installed.$(RIDForWorkload).stamp" /> + <_SdkWithWorkloadToInstall StampPath="%(InstallPath)\.workload-installed.$(RuntimeIdentifier).stamp" /> @@ -218,7 +216,4 @@ - - - diff --git a/eng/testing/workloads-wasi.targets b/eng/testing/workloads-wasi.targets deleted file mode 100644 index 356cad0fa06eff..00000000000000 --- a/eng/testing/workloads-wasi.targets +++ /dev/null @@ -1,24 +0,0 @@ - - - - - _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) - _GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) - - - - - - - - - - - - diff --git a/eng/testing/workloads-wasm.targets b/eng/testing/workloads-wasm.targets deleted file mode 100644 index eebcdfcf45604e..00000000000000 --- a/eng/testing/workloads-wasm.targets +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg - - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';')" - Descriptor="Ref pack"/> - - - <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> - <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> - <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RIDForWorkload)" /> - <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> - - <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" - Properties="@(_PropsForAOTCrossBuild,';')" - Descriptor="AOT Cross compiler"/> - - - diff --git a/eng/testing/workoads-wasi.targets b/eng/testing/workoads-wasi.targets deleted file mode 100644 index 8b335fbcca1e24..00000000000000 --- a/eng/testing/workoads-wasi.targets +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg - - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';')" - Descriptor="Ref pack"/> - - - <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> - <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> - <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> - - <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" - Properties="@(_PropsForAOTCrossBuild,';')" - Descriptor="AOT Cross compiler"/> - - - diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index b37bae89cb86cc..af6fc30ae1ba87 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -94,16 +94,15 @@ --interpreter - + $(ArtifactsBinDir)dotnet-none\ $([MSBuild]::NormalizeDirectory($(SdkWithNoWorkloadForTestingPath))) $(SdkWithNoWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp $(SdkWithNoWorkloadForTestingPath)workload.stamp - $(ArtifactsBinDir)dotnet-latest\ - - + $(ArtifactsBinDir)dotnet-latest\ + $(ArtifactsBinDir)dotnet-latest\ $([MSBuild]::NormalizeDirectory($(SdkWithWorkloadForTestingPath))) $(SdkWithWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp diff --git a/src/mono/wasi/Makefile b/src/mono/wasi/Makefile index 3554f20f99a202..7fc6904bc0702f 100644 --- a/src/mono/wasi/Makefile +++ b/src/mono/wasi/Makefile @@ -65,7 +65,7 @@ run-tests-%: $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) run-build-tests: - WASI_SDK_PATH=$(WASI_SDK_PATH) $(DOTNET) build $(TOP)/src/mono/wasi//Wasi.Build.Tests/ /t:Test /bl /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) + WASI_SDK_PATH=$(WASI_SDK_PATH) $(DOTNET) build $(TOP)/src/mono/wasi//Wasi.Build.Tests/ /t:Test /bl $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) build-debugger-tests-helix: $(DOTNET) build -restore -bl:$(TOP)/artifacts/log/$(CONFIG)/Wasm.Debugger.Tests.binlog \ diff --git a/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props b/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props index b890588c22160c..102dd7c86919d9 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props +++ b/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props @@ -4,16 +4,7 @@ BuildWasmApps true Wasi.Build.Tests - - wasi-wasm - wasi - wasm - - $(OutputRID) - true - true - diff --git a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj index 19c41e2b440015..cc27e159d25e6e 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj +++ b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj @@ -1,7 +1,6 @@ $(NetCoreAppToolCurrent) - true true true @@ -104,7 +103,7 @@ - /workspaces/runtime/.dotnet/dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml + dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml $(RunScriptCommand) %24HELIX_XUNIT_ARGS From ad2b9baec5018ee5bbc3f4095880cd29a58e182f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 20:54:38 -0500 Subject: [PATCH 049/189] [wasm] Extract workload testing bits from tests.*.targets into separate files, so they can be used outside wasm test projects --- eng/testing/tests.browser.targets | 88 ------------------------ eng/testing/tests.wasi.targets | 17 ----- eng/testing/tests.wasm.targets | 26 -------- eng/testing/workloads-browser.targets | 96 +++++++++++++++++++++++++++ eng/testing/workloads-testing.targets | 11 ++- eng/testing/workloads-wasi.targets | 24 +++++++ eng/testing/workloads-wasm.targets | 28 ++++++++ src/libraries/Directory.Build.props | 5 +- 8 files changed, 158 insertions(+), 137 deletions(-) create mode 100644 eng/testing/workloads-browser.targets create mode 100644 eng/testing/workloads-wasi.targets create mode 100644 eng/testing/workloads-wasm.targets diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index ea561e42ccaff4..005a43083a823f 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -23,7 +23,6 @@ true false - _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) true - _GetRuntimePackNuGetsToBuild;_GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasmBuildApp;_PrepareForAOTOnHelix true false @@ -272,90 +270,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - <_DefaultBuildVariant Condition="'$(MonoWasmBuildVariant)' == 'multithread'">.multithread. - <_DefaultBuildVariant Condition="'$(_DefaultBuildVariant)' == ''">. - - <_DefaultRuntimePackNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono$(_DefaultBuildVariant)$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg - - - - <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RuntimeIdentifier).*$(PackageVersionForWorkloadManifests).nupkg" /> - <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.*.$(RuntimeIdentifier).*$(PackageVersionForWorkloadManifests).nupkg" /> - <_RuntimePackNugetAvailable Remove="@(_RuntimePackNugetAvailable)" Condition="$([System.String]::new('%(_RuntimePackNugetAvailable.FileName)').EndsWith('.symbols'))" /> - - - - - - <_BuildVariants Include="multithread" Condition="'$(_DefaultBuildVariant)' != '.multithread.'" /> - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.%(_BuildVariants.Identity).$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" - Dependencies="$(_DefaultRuntimePackNuGetPath)" - Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=%(_BuildVariants.Identity)" - Descriptor="runtime pack for %(_BuildVariants.Identity)" - Condition="'%(_BuildVariants.Identity)' != ''"/> - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=" - Dependencies="$(_DefaultRuntimePackNuGetPath)" - Descriptor="single threaded runtime pack" - Condition="'$(_DefaultBuildVariant)' != '.'" /> - - - - diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 9f4068a1187a94..8516230d7e6da6 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -13,8 +13,6 @@ >true - _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) - _GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasi', 'wasi-sdk')) <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasiBuildApp;_PrepareForAOTOnHelix @@ -158,19 +156,4 @@ - - - - - - - - - diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index e76f1acb374cde..bf29d930d02f59 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -186,30 +186,4 @@ - - - - - <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg - - - - <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" - Properties="@(_DefaultPropsForNuGetBuild, ';')" - Descriptor="Ref pack"/> - - - <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> - <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> - <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RuntimeIdentifier)" /> - <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> - - <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" - Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" - Properties="@(_PropsForAOTCrossBuild,';')" - Descriptor="AOT Cross compiler"/> - - diff --git a/eng/testing/workloads-browser.targets b/eng/testing/workloads-browser.targets new file mode 100644 index 00000000000000..270267385bab11 --- /dev/null +++ b/eng/testing/workloads-browser.targets @@ -0,0 +1,96 @@ + + + + + _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) + _GetRuntimePackNuGetsToBuild;_GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) + + + + + + + + + + + + + + + + + + + + + + + + + + + <_DefaultBuildVariant Condition="'$(MonoWasmBuildVariant)' == 'multithread'">.multithread. + <_DefaultBuildVariant Condition="'$(_DefaultBuildVariant)' == ''">. + + <_DefaultRuntimePackNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono$(_DefaultBuildVariant)$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg + + + + <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RIDForWorkload).*$(PackageVersionForWorkloadManifests).nupkg" /> + <_RuntimePackNugetAvailable Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.*.$(RIDForWorkload).*$(PackageVersionForWorkloadManifests).nupkg" /> + <_RuntimePackNugetAvailable Remove="@(_RuntimePackNugetAvailable)" Condition="$([System.String]::new('%(_RuntimePackNugetAvailable.FileName)').EndsWith('.symbols'))" /> + + + + + + <_BuildVariants Include="multithread" Condition="'$(_DefaultBuildVariant)' != '.multithread.'" /> + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.%(_BuildVariants.Identity).$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" + Dependencies="$(_DefaultRuntimePackNuGetPath)" + Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=%(_BuildVariants.Identity)" + Descriptor="runtime pack for %(_BuildVariants.Identity)" + Condition="'%(_BuildVariants.Identity)' != ''"/> + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.Mono.$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';');MonoWasmBuildVariant=" + Dependencies="$(_DefaultRuntimePackNuGetPath)" + Descriptor="single threaded runtime pack" + Condition="'$(_DefaultBuildVariant)' != '.'" /> + + + + + diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets index 5c4e021e7a8849..636f65052eedd1 100644 --- a/eng/testing/workloads-testing.targets +++ b/eng/testing/workloads-testing.targets @@ -55,8 +55,8 @@ <_DefaultPropsForNuGetBuild Include="Configuration=$(Configuration)" /> - <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOS)" /> - <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitecture)" /> + <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOSForWorkload)" /> + <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitectureForWorkload)" /> <_DefaultPropsForNuGetBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> @@ -150,6 +150,8 @@ + + @@ -157,7 +159,7 @@ <_SdkWithWorkloadToInstall Include="@(WorkloadCombinationsToInstall)" /> <_SdkWithWorkloadToInstall InstallPath="$(_SdkForWorkloadTestingBasePath)\dotnet-%(Identity)" /> - <_SdkWithWorkloadToInstall StampPath="%(InstallPath)\.workload-installed.$(RuntimeIdentifier).stamp" /> + <_SdkWithWorkloadToInstall StampPath="%(InstallPath)\.workload-installed.$(RIDForWorkload).stamp" /> @@ -216,4 +218,7 @@ + + + diff --git a/eng/testing/workloads-wasi.targets b/eng/testing/workloads-wasi.targets new file mode 100644 index 00000000000000..356cad0fa06eff --- /dev/null +++ b/eng/testing/workloads-wasi.targets @@ -0,0 +1,24 @@ + + + + + _GetWorkloadsToInstall;$(GetWorkloadInputsDependsOn) + _GetNugetsForAOT;$(GetNuGetsToBuildForWorkloadTestingDependsOn) + + + + + + + + + + + + diff --git a/eng/testing/workloads-wasm.targets b/eng/testing/workloads-wasm.targets new file mode 100644 index 00000000000000..eebcdfcf45604e --- /dev/null +++ b/eng/testing/workloads-wasm.targets @@ -0,0 +1,28 @@ + + + + + + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RIDForWorkload).$(PackageVersionForWorkloadManifests).nupkg + + + + <_NuGetsToBuild Include="$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Ref.$(PackageVersionForWorkloadManifests).nupkg" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.Ref.sfxproj" + Properties="@(_DefaultPropsForNuGetBuild, ';')" + Descriptor="Ref pack"/> + + + <_PropsForAOTCrossBuild Include="@(_DefaultPropsForNuGetBuild)" /> + <_PropsForAOTCrossBuild Include="TestingWorkloads=true" /> + <_PropsForAOTCrossBuild Include="RuntimeIdentifier=$(NETCoreSdkRuntimeIdentifier)" /> + <_PropsForAOTCrossBuild Include="TargetCrossRid=$(RIDForWorkload)" /> + <_PropsForAOTCrossBuild Include="DisableSourceLink=true" /> + + <_NuGetsToBuild Include="$(_AOTCrossNuGetPath)" + Project="$(InstallerProjectRoot)pkg/sfx/Microsoft.NETCore.App\Microsoft.NETCore.App.MonoCrossAOT.sfxproj" + Properties="@(_PropsForAOTCrossBuild,';')" + Descriptor="AOT Cross compiler"/> + + + diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index af6fc30ae1ba87..8249580bdb2d66 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -94,15 +94,14 @@ --interpreter - + $(ArtifactsBinDir)dotnet-none\ $([MSBuild]::NormalizeDirectory($(SdkWithNoWorkloadForTestingPath))) $(SdkWithNoWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp $(SdkWithNoWorkloadForTestingPath)workload.stamp - $(ArtifactsBinDir)dotnet-latest\ - $(ArtifactsBinDir)dotnet-latest\ + $(ArtifactsBinDir)dotnet-latest\ $([MSBuild]::NormalizeDirectory($(SdkWithWorkloadForTestingPath))) $(SdkWithWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp From 9ca4eaac4797992a5366e4435022339062ff8b62 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 20:55:13 -0500 Subject: [PATCH 050/189] [wasm] Wasm.Build.Tests - build for host os/arch instead of wasm The tests themselves are built to run on coreclr, but each test would build+run tests on wasm. The CI builds are structured such that WBT gets built with `TargetOS=browser` and `TargetArchitecture=wasm`, which would cause the core assemblies to be bundled from the wasm runtime pack, resulting in failures like: ``` System.AggregateException : One or more errors occurred. (System.Diagnostics.Process is not supported on this platform.) ---- System.PlatformNotSupportedException : System.Diagnostics.Process is not supported on this platform. Stack Trace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task`1.get_Result() ``` To make sure that WBT is built for the host os/arch we clear the two properties. --- src/mono/browser/Makefile | 2 +- .../wasi/Wasi.Build.Tests/Directory.Build.props | 16 +++++++++++++++- .../Wasi.Build.Tests/Wasi.Build.Tests.csproj | 5 +++++ .../wasm/Wasm.Build.Tests/Directory.Build.props | 11 ++++++++++- .../Wasm.Build.Tests/Wasm.Build.Tests.csproj | 6 ++++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/mono/browser/Makefile b/src/mono/browser/Makefile index c650564d7d258d..6cefc09ad43598 100644 --- a/src/mono/browser/Makefile +++ b/src/mono/browser/Makefile @@ -105,7 +105,7 @@ run-tests-%: EMSDK_PATH=$(EMSDK_PATH) PATH="$(JSVU):$(PATH)" $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) run-build-tests: - PATH="$(JSVU):$(PATH)" $(DOTNET) build $(TOP)/src/mono/wasm/Wasm.Build.Tests/ /t:Test /bl:$(LOG_PATH)/Wasm.Build.Tests.binlog $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) + PATH="$(JSVU):$(PATH)" $(DOTNET) build $(TOP)/src/mono/wasm/Wasm.Build.Tests/ /t:Test /bl:$(LOG_PATH)/Wasm.Build.Tests.binlog /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) run-browser-tests-%: PATH="$(GECKODRIVER):$(CHROMEDRIVER):$(PATH)" XHARNESS_COMMAND="test-browser --browser=$(XHARNESS_BROWSER)" $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) diff --git a/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props b/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props index 102dd7c86919d9..8da854c6d3fc2f 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props +++ b/src/mono/wasi/Wasi.Build.Tests/Directory.Build.props @@ -1,10 +1,24 @@ - + + + + + BuildWasmApps true Wasi.Build.Tests + + wasi-wasm + wasi + wasm + + $(OutputRID) + true + true + diff --git a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj index cc27e159d25e6e..f5a98f4f0e01f4 100644 --- a/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj +++ b/src/mono/wasi/Wasi.Build.Tests/Wasi.Build.Tests.csproj @@ -23,9 +23,14 @@ false TARGET_WASI <_MonoSrcWasmDir>$([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm')) + + true + RunScriptTemplate.cmd diff --git a/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props b/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props index dd08eb42f531b2..8e6cc16d73c1a7 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props +++ b/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props @@ -1,9 +1,18 @@ - + + + + + BuildWasmApps true Wasm.Build.Tests + + browser-wasm + browser + wasm diff --git a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 3b8e184d96637b..1c4a27a1acd377 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -15,7 +15,9 @@ false true + + true true @@ -25,8 +27,12 @@ + + <_WasmBrowserPathForTests Condition="'$(BROWSER_PATH_FOR_TESTS)' != ''">$(BROWSER_PATH_FOR_TESTS) + <_WasmBrowserPathForTests Condition="'$(_WasmBrowserPathForTests)' == '' and '$(InstallChromeForTests)' == 'true'">$(ChromeBinaryPath) + RunScriptTemplate.cmd RunScriptTemplate.sh From 1c4bf91c14eeb896cbaac988500ece6361119bd3 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 20:58:21 -0500 Subject: [PATCH 051/189] [wasm] fix local builds - makefile --- src/mono/browser/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/browser/Makefile b/src/mono/browser/Makefile index 6cefc09ad43598..6b756c64e33e6d 100644 --- a/src/mono/browser/Makefile +++ b/src/mono/browser/Makefile @@ -34,7 +34,7 @@ all: build-all # EMSCRIPTEN_VERSION := $(shell cat $(TOP)/src/mono/browser/emscripten-version.txt) -EMSDK_LOCAL_PATH=emsdk +EMSDK_LOCAL_PATH=$(CURDIR)/emsdk EMCC=source $(EMSDK_PATH)/emsdk_env.sh 2>/dev/null && emcc .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION): @@ -42,7 +42,7 @@ EMCC=source $(EMSDK_PATH)/emsdk_env.sh 2>/dev/null && emcc git clone https://github.com/emscripten-core/emsdk.git $(EMSDK_LOCAL_PATH) cd $(EMSDK_LOCAL_PATH) && ./emsdk install $(EMSCRIPTEN_VERSION) cd $(EMSDK_LOCAL_PATH) && ./emsdk activate $(EMSCRIPTEN_VERSION) - python3 ./sanitize.py $(EMSDK_PATH) + python3 ./sanitize.py $(EMSDK_LOCAL_PATH) touch $@ provision-wasm: .stamp-wasm-install-and-select-$(EMSCRIPTEN_VERSION) From 2a4fd5df5869ad51e6de3fb15f1a532359550c62 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 20:59:06 -0500 Subject: [PATCH 052/189] [wasm] Run WBT with dotnet 8 (global.json) .. instead of dotnet 9, with which it seems to segfault. --- src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.cmd | 1 - src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.cmd b/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.cmd index ff131266cec528..e088aa4861df49 100644 --- a/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.cmd +++ b/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.cmd @@ -64,6 +64,5 @@ if [%HELIX_CORRELATION_PAYLOAD%] NEQ [] ( set _SDK_DIR=%BASE_DIR%\%SDK_DIR_NAME% ) -set "PATH=%_SDK_DIR%;%PATH%" set "SDK_FOR_WORKLOAD_TESTING_PATH=%_SDK_DIR%" EXIT /b 0 diff --git a/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.sh b/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.sh index 67e87c9afe2912..2c23e8a4185f6e 100644 --- a/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.sh +++ b/src/mono/wasm/Wasm.Build.Tests/data/RunScriptTemplate.sh @@ -47,7 +47,6 @@ function set_env_vars() _SDK_DIR=$BASE_DIR/$SDK_DIR_NAME fi - export PATH=$_SDK_DIR:$PATH export SDK_FOR_WORKLOAD_TESTING_PATH=$_SDK_DIR } From e282f4b6cb7d66fff9b151f2e06193ea1836f81f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 21:12:17 -0500 Subject: [PATCH 053/189] [wasm] fix local debugger tests run --- .../browser/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/browser/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj b/src/mono/browser/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj index 2f1bbba894b2e5..cb58cc51407c52 100644 --- a/src/mono/browser/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj +++ b/src/mono/browser/debugger/DebuggerTestSuite/DebuggerTestSuite.csproj @@ -50,7 +50,7 @@ target ensures that the correct tfm is used. --> Date: Mon, 8 Jan 2024 23:17:35 -0500 Subject: [PATCH 054/189] [wasm] Browser.targets: track tfm change for tasks --- .../build/Microsoft.NET.Sdk.WebAssembly.Browser.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index f5b49c75cc3025..28517e9c58ba3b 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -45,7 +45,7 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildThisFileDirectory)..\ - <_WebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net8.0 + <_WebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net9.0 <_WebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472 <_WebAssemblySdkTasksAssembly>$(WebAssemblySdkDirectoryRoot)tools\$(_WebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.dll From 51ca44e6fe32407644ec3ee8c0762959d1a551cd Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Jan 2024 23:22:48 -0500 Subject: [PATCH 055/189] [wasm] fix wbt build --- src/mono/wasm/Wasm.Build.Tests/Directory.Build.props | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props b/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props index 8e6cc16d73c1a7..3e2949f2375591 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props +++ b/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props @@ -16,4 +16,9 @@ + + $(OutputRID) + true + true + From a54adf7f51b32d283bdd6f11767c065ed8c9a1e4 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 9 Jan 2024 01:19:07 -0500 Subject: [PATCH 056/189] [wasm] WBT: don't use publishTrimmed=true --- src/mono/wasm/Wasm.Build.Tests/Directory.Build.props | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props b/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props index 3e2949f2375591..7bc1738eb86741 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props +++ b/src/mono/wasm/Wasm.Build.Tests/Directory.Build.props @@ -19,6 +19,5 @@ $(OutputRID) true - true From 9580bfd010ba17df8eb42f99aebf167553f6e9e5 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 9 Jan 2024 16:01:19 -0500 Subject: [PATCH 057/189] Set workload testing specific properties in workloads-testing.targets --- eng/testing/workloads-testing.targets | 14 ++++++++++++++ src/libraries/Directory.Build.props | 14 -------------- src/libraries/sendtohelix-wasm.targets | 2 ++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets index 636f65052eedd1..adc6d20f4b86ce 100644 --- a/eng/testing/workloads-testing.targets +++ b/eng/testing/workloads-testing.targets @@ -5,6 +5,20 @@ true + + $(ArtifactsBinDir)dotnet-none\ + $([MSBuild]::NormalizeDirectory($(SdkWithNoWorkloadForTestingPath))) + + $(SdkWithNoWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp + $(SdkWithNoWorkloadForTestingPath)workload.stamp + + $(ArtifactsBinDir)dotnet-latest\ + $([MSBuild]::NormalizeDirectory($(SdkWithWorkloadForTestingPath))) + + $(SdkWithWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp + $(SdkWithWorkloadForTestingPath)workload.stamp + + <_SdkForWorkloadTestingBasePath>$(ArtifactsBinDir) <_SdkWithNoWorkloadPath>$([MSBuild]::NormalizeDirectory($(_SdkForWorkloadTestingBasePath), 'dotnet-none')) diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 8249580bdb2d66..cc1443588858a8 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -94,20 +94,6 @@ --interpreter - - $(ArtifactsBinDir)dotnet-none\ - $([MSBuild]::NormalizeDirectory($(SdkWithNoWorkloadForTestingPath))) - - $(SdkWithNoWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp - $(SdkWithNoWorkloadForTestingPath)workload.stamp - - $(ArtifactsBinDir)dotnet-latest\ - $([MSBuild]::NormalizeDirectory($(SdkWithWorkloadForTestingPath))) - - $(SdkWithWorkloadForTestingPath)version-$(SdkVersionForWorkloadTesting).stamp - $(SdkWithWorkloadForTestingPath)workload.stamp - - diff --git a/src/libraries/sendtohelix-wasm.targets b/src/libraries/sendtohelix-wasm.targets index 12a797477222d3..d9c2e3aead0845 100644 --- a/src/libraries/sendtohelix-wasm.targets +++ b/src/libraries/sendtohelix-wasm.targets @@ -1,4 +1,6 @@ + + <_workItemTimeout Condition="'$(Scenario)' == 'BuildWasmApps' and '$(_workItemTimeout)' == ''">01:30:00 <_workItemTimeout Condition="'$(NeedsToBuildWasmAppsOnHelix)' == 'true'">01:00:00 From 8f50adcf8c096f20ffd1b37554a8046bc9ab4308 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 9 Jan 2024 16:01:49 -0500 Subject: [PATCH 058/189] [wasm] Fix checking for icudt files against the runtime pack The files in the appbundle were being compared against the file copied to the bindir for Wasm?Build.Tests, like `artifacts/bin/Wasm.Build.Tests/Release/net8.0/browser-wasm`. But since WBT is not being built to target wasm anymore, these files are never copied to the bindir. Instead, construct the path to runtime pack and check against that. This is the correct behavior. --- src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs index 50dac9c67a1e7a..1ee58888f47a10 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs @@ -382,7 +382,13 @@ public void AssertIcuAssets(AssertBundleOptionsBase assertOptions) IEnumerable actual = Directory.EnumerateFiles(assertOptions.BinFrameworkDir, "icudt*dat"); AssertFilesOnDisk(expected, actual); if (assertOptions.GlobalizationMode is GlobalizationMode.PredefinedIcu) - TestUtils.AssertSameFile(assertOptions.PredefinedIcudt!, actual.Single()); + { + string srcPath = assertOptions.PredefinedIcudt!; + string runtimePackDir = BuildTestBase.s_buildEnv.GetRuntimeNativeDir(assertOptions.TargetFramework, assertOptions.RuntimeType); + if (!Path.IsPathRooted(srcPath)) + srcPath = Path.Combine(runtimePackDir, assertOptions.PredefinedIcudt!); + TestUtils.AssertSameFile(srcPath, actual.Single()); + } } public void AssertBootJson(AssertBundleOptionsBase options) From 25745295725cbf544ead7f2ce8c1706e2bedef1f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 9 Jan 2024 16:06:25 -0500 Subject: [PATCH 059/189] cleanup --- .../wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs | 10 +++++----- src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs index 09132ef2538bcc..aa196570151856 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs @@ -21,7 +21,7 @@ public IcuShardingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture [Theory] [InlineData("Debug", "icudt.dat")] - [InlineData("Release", "icudt.dat")] + [InlineData("Release", "icudt.dat")] [InlineData("Debug", "icudt_CJK.dat")] [InlineData("Release", "icudt_CJK.dat")] public async Task CustomIcuFileFromRuntimePack(string config, string fileName) @@ -37,7 +37,7 @@ public async Task CustomIcuFileFromRuntimePack(string config, string fileName) ); AddItemsPropertiesToProject( projectFile, - extraProperties: + extraProperties: $"{fileName}"); (CommandResult res, string logPath) = BlazorBuild(buildOptions); @@ -55,7 +55,7 @@ public void NonExistingCustomFileAssertError(string config, string fileName, boo string projectFile = CreateBlazorWasmTemplateProject(id); AddItemsPropertiesToProject( projectFile, - extraProperties: + extraProperties: $"{fileName}"); try @@ -96,7 +96,7 @@ public async Task CustomFileNotFromRuntimePackAbsolutePath(string config) string projectFile = CreateBlazorWasmTemplateProject(id); AddItemsPropertiesToProject( projectFile, - extraProperties: + extraProperties: $"{IcuTestsBase.CustomIcuPath}"); (CommandResult res, string logPath) = BlazorBuild( @@ -109,4 +109,4 @@ public async Task CustomFileNotFromRuntimePackAbsolutePath(string config) )); await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config }); } -} \ No newline at end of file +} diff --git a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs index 1ee58888f47a10..95de44c3347604 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs @@ -380,7 +380,7 @@ public void AssertIcuAssets(AssertBundleOptionsBase assertOptions) } IEnumerable actual = Directory.EnumerateFiles(assertOptions.BinFrameworkDir, "icudt*dat"); - AssertFilesOnDisk(expected, actual); + AssertFileNames(expected, actual); if (assertOptions.GlobalizationMode is GlobalizationMode.PredefinedIcu) { string srcPath = assertOptions.PredefinedIcudt!; @@ -473,7 +473,7 @@ public static BootJsonData ParseBootData(string bootJsonPath) return config; } - private void AssertFilesOnDisk(IEnumerable expected, IEnumerable actual) + private void AssertFileNames(IEnumerable expected, IEnumerable actual) { expected = expected.Order().Select(f => Path.GetFileName(f)).Distinct(); var actualFileNames = actual.Order().Select(f => Path.GetFileName(f)); From 9bc55b78fcf321f77093e6a039430f4348f7fb96 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 9 Jan 2024 16:07:02 -0500 Subject: [PATCH 060/189] WBT: Update TargetFrameworkForTasks=net9.0 --- src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs index 52438daac5ddf9..a384b07bbebdd2 100644 --- a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs @@ -27,7 +27,7 @@ public abstract class BuildTestBase : IClassFixture Date: Tue, 9 Jan 2024 21:38:18 +0000 Subject: [PATCH 061/189] WBT: Set chromedriver path --- src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 1c4a27a1acd377..9ebf19950681e2 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -52,6 +52,11 @@ + + + + + From 238ccece59a16d54cf068e30e0d9cf366a1b9332 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 9 Jan 2024 22:04:42 +0000 Subject: [PATCH 062/189] eng/targetingpack.targets: Add wasi-wasm only for the latest tfm addresses review feedback from @ viktorhofer . --- eng/targetingpacks.targets | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets index ab4c6c3e72852c..befd249f231d2a 100644 --- a/eng/targetingpacks.targets +++ b/eng/targetingpacks.targets @@ -53,9 +53,13 @@ RuntimeFrameworkName="$(LocalFrameworkOverrideName)" LatestRuntimeFrameworkVersion="$(ProductVersion)" RuntimePackNamePatterns="$(LocalFrameworkOverrideName).Runtime.Mono.**RID**" - RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;browser-wasm;wasi-wasm;ios-arm64;ios-arm;iossimulator-arm64;iossimulator-x64;iossimulator-x86;tvos-arm64;tvossimulator-arm64;tvossimulator-x64;maccatalyst-x64;maccatalyst-arm64;android-arm64;android-arm;android-x64;android-x86" + RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;win-arm64;win-x64;win-x86;linux-musl-arm;osx-arm64;linux-s390x;linux-bionic-arm;linux-bionic-arm64;linux-bionic-x64;linux-bionic-x86;browser-wasm;ios-arm64;ios-arm;iossimulator-arm64;iossimulator-x64;iossimulator-x86;tvos-arm64;tvossimulator-arm64;tvossimulator-x64;maccatalyst-x64;maccatalyst-arm64;android-arm64;android-arm;android-x64;android-x86" RuntimePackLabels="Mono" Condition="'$(UseLocalTargetingRuntimePack)' == 'true' and ('@(KnownRuntimePack)' == '' or @(KnownRuntimePack->WithMetadataValue('Identity', 'Microsoft.NETCore.App')->WithMetadataValue('RuntimePackLabels', 'Mono')->WithMetadataValue('TargetFramework', '$(NetCoreAppCurrent)')) == '')" /> + + - - - - - - - From 73d7efdd0fff1ed9200aba3888fb75a4313c500f Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 16 Jan 2024 21:56:08 +0100 Subject: [PATCH 063/189] Update CompatibilitySuppressions.xml --- src/tools/illink/src/linker/CompatibilitySuppressions.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml index 1da6cfd6985a85..4a0a6296c4e2f3 100644 --- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml +++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml @@ -768,8 +768,6 @@ CP0002 M:Mono.Linker.AnnotationStore.IsRootAssembly(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 @@ -838,8 +836,6 @@ CP0002 M:Mono.Linker.AnnotationStore.SetRootAssembly(Mono.Cecil.AssemblyDefinition) - ref/net8.0/illink.dll - lib/net8.0/illink.dll CP0002 From e8026dd667035b6b8fd8df8f744451dfb13aaccf Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 10 Jan 2024 02:44:53 -0500 Subject: [PATCH 064/189] [wasm] WBT ProcessExtensions: Use Process.Kill(true) .. instead of an old custom implementation. --- .../Common/ProcessExtensions.cs | 78 ------------------- .../Wasm.Build.Tests/Common/ToolCommand.cs | 2 +- 2 files changed, 1 insertion(+), 79 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs b/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs index 9a5f87b6c4402a..aed755bbc9f632 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs @@ -12,84 +12,6 @@ namespace Wasm.Build.Tests { internal static class ProcessExtensions { -#if NET451 - private static readonly bool _isWindows = true; -#else - private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#endif - private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); - - public static void KillTree(this Process process) - { - process.KillTree(_defaultTimeout); - } - - public static void KillTree(this Process process, TimeSpan timeout) - { - string stdout; - if (_isWindows) - { - RunProcessAndWaitForExit( - "taskkill", - $"/T /F /PID {process.Id}", - timeout, - out stdout); - } - else - { - var children = new HashSet(); - GetAllChildIdsUnix(process.Id, children, timeout); - foreach (var childId in children) - { - KillProcessUnix(childId, timeout); - } - KillProcessUnix(process.Id, timeout); - } - } - - private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) - { - string stdout; - var exitCode = RunProcessAndWaitForExit( - "pgrep", - $"-P {parentId}", - timeout, - out stdout); - - if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) - { - using (var reader = new StringReader(stdout)) - { - while (true) - { - var text = reader.ReadLine(); - if (text == null) - { - return; - } - - int id; - if (int.TryParse(text, out id)) - { - children.Add(id); - // Recursively get the children - GetAllChildIdsUnix(id, children, timeout); - } - } - } - } - } - - private static void KillProcessUnix(int processId, TimeSpan timeout) - { - string stdout; - RunProcessAndWaitForExit( - "kill", - $"-TERM {processId}", - timeout, - out stdout); - } - private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) { var startInfo = new ProcessStartInfo diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/ToolCommand.cs b/src/mono/wasm/Wasm.Build.Tests/Common/ToolCommand.cs index a308c7ba668cb7..eb2b80302b1161 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/ToolCommand.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/ToolCommand.cs @@ -95,7 +95,7 @@ public virtual void Dispose() { if (CurrentProcess is not null && !CurrentProcess.HasExited) { - CurrentProcess.KillTree(); + CurrentProcess.Kill(entireProcessTree: true); CurrentProcess.Dispose(); CurrentProcess = null; } From dc34fddea622be6f2ebe899e7288299a88408c1d Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Wed, 10 Jan 2024 02:48:18 -0500 Subject: [PATCH 065/189] [wasm] WBT: don't try to read ExitCode after process.Kill `Process.Kill` returns immediately, and WaitOnExit needs to be used to wait for the actual exit. --- .../wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs b/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs index aed755bbc9f632..b38079e2452543 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/ProcessExtensions.cs @@ -28,13 +28,12 @@ private static int RunProcessAndWaitForExit(string fileName, string arguments, T if (process.WaitForExit((int)timeout.TotalMilliseconds)) { stdout = process.StandardOutput.ReadToEnd(); - } - else - { - process.Kill(); + return process.ExitCode; } - return process.ExitCode; + process.Kill(entireProcessTree: true); + // sigkill - 128+9(sigkill) + return 137; } public static Task StartAndWaitForExitAsync(this Process subject) From 7bb42b4ca054144b1b1c049db73e36e9496fe79f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 16 Jan 2024 17:48:27 -0500 Subject: [PATCH 066/189] [wasm] WBT: disable failing tests Issue: https://github.com/dotnet/runtime/issues/97054 --- src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs | 2 ++ src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs | 2 ++ src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs | 5 ++++- src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests3.cs | 1 + .../Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs | 1 + src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs | 2 ++ .../wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs | 1 + .../wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs | 7 ++++--- .../Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs | 1 + .../Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs | 2 ++ .../TestAppScenarios/LibraryInitializerTests.cs | 2 ++ .../TestAppScenarios/SatelliteLoadingTests.cs | 1 + 12 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index e7be07f71c89e1..0157118771a661 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -26,6 +26,7 @@ public BuildPublishTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur [Theory, TestCategory("no-workload")] [InlineData("Debug")] [InlineData("Release")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task DefaultTemplate_WithoutWorkload(string config) { string id = $"blz_no_workload_{config}_{GetRandomId()}_{s_unicodeChar}"; @@ -177,6 +178,7 @@ void AssertResourcesDlls(string basePath) [Theory] [InlineData("", true)] // Default case [InlineData("false", false)] // the other case + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task Test_WasmStripILAfterAOT(string stripILAfterAOT, bool expectILStripping) { string config = "Release"; diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs index aa196570151856..7bfa39e3fe52b0 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuShardingTests.cs @@ -24,6 +24,7 @@ public IcuShardingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture [InlineData("Release", "icudt.dat")] [InlineData("Debug", "icudt_CJK.dat")] [InlineData("Release", "icudt_CJK.dat")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task CustomIcuFileFromRuntimePack(string config, string fileName) { string id = $"blz_customFromRuntimePack_{config}_{GetRandomId()}"; @@ -90,6 +91,7 @@ public void NonExistingCustomFileAssertError(string config, string fileName, boo [Theory] [InlineData("Debug")] [InlineData("Release")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task CustomFileNotFromRuntimePackAbsolutePath(string config) { string id = $"blz_invalidCustomIcu_{config}_{GetRandomId()}"; diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs index 728eb96386b2ec..7169e14b36ae4c 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs @@ -26,6 +26,7 @@ public IcuTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildCo [InlineData("Release", false)] [InlineData("Release", true)] [InlineData("Release", null)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task HybridWithInvariant(string config, bool? invariant) { string id = $"blz_hybrid_{config}_{GetRandomId()}"; @@ -64,6 +65,7 @@ public async Task HybridWithInvariant(string config, bool? invariant) [InlineData("Release", false)] [InlineData("Release", true)] [InlineData("Release", null)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task HybridWithFullIcuFromRuntimePack(string config, bool? fullIcu) { string id = $"blz_hybrid_{config}_{GetRandomId()}"; @@ -101,6 +103,7 @@ public async Task HybridWithFullIcuFromRuntimePack(string config, bool? fullIcu) [InlineData("Release", false)] [InlineData("Release", true)] [InlineData("Release", null)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task FullIcuFromRuntimePackWithInvariant(string config, bool? invariant) { string id = $"blz_hybrid_{config}_{GetRandomId()}"; @@ -131,4 +134,4 @@ public async Task FullIcuFromRuntimePackWithInvariant(string config, bool? invar await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config }); } -} \ No newline at end of file +} diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests3.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests3.cs index 10717b334174b9..3bd5ddd8bd02a6 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests3.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests3.cs @@ -30,6 +30,7 @@ public MiscTests3(ITestOutputHelper output, SharedBuildPerTestClassFixture build [InlineData("Release", /*build*/false, /*publish*/true)] [InlineData("Release", /*build*/true, /*publish*/true)] [ActiveIssue("https://github.com/dotnet/runtime/issues/87877", TestPlatforms.Windows)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task WithDllImportInMainAssembly(string config, bool build, bool publish) { // Based on https://github.com/dotnet/runtime/issues/59255 diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs index c28f18a3c57469..d5b96055ddc6d8 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs @@ -42,6 +42,7 @@ public SimpleMultiThreadedTests(ITestOutputHelper output, SharedBuildPerTestClas // [InlineData("Debug", true)] [InlineData("Release", false)] // [InlineData("Release", true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task BlazorPublishRunTest(string config, bool aot) { string id = $"blazor_mt_{config}_{GetRandomId()}"; diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs index fdce8ea756f844..74d1fdf3f35e2a 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs @@ -39,6 +39,7 @@ public async Task BlazorBuildRunTest(string config) [InlineData("Debug", /*appendRID*/ true, /*useArtifacts*/ true)] [InlineData("Debug", /*appendRID*/ false, /*useArtifacts*/ true)] [InlineData("Debug", /*appendRID*/ false, /*useArtifacts*/ false)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task BlazorBuildAndRunForDifferentOutputPaths(string config, bool appendRID, bool useArtifacts) { string id = $"{config}_{GetRandomId()}"; @@ -81,6 +82,7 @@ public async Task BlazorBuildAndRunForDifferentOutputPaths(string config, bool a [InlineData("Debug", true)] [InlineData("Release", false)] [InlineData("Release", true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task BlazorPublishRunTest(string config, bool aot) { string id = $"blazor_{config}_{GetRandomId()}"; diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs index b811d2b7dc9169..643b2474847940 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs @@ -76,6 +76,7 @@ public static TheoryData InvariantGlobalizationTestData(bool [Theory, TestCategory("no-workload")] [MemberData(nameof(InvariantGlobalizationTestData), parameters: /*publish*/ false)] [MemberData(nameof(InvariantGlobalizationTestData), parameters: /*publish*/ true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task WorkloadNotRequiredForInvariantGlobalization(string config, bool invariant, bool publish) { string id = $"props_req_workload_{(publish ? "publish" : "build")}_{GetRandomId()}"; diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs index 4a93f148d0cb9c..98d0331bb620be 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs @@ -254,9 +254,9 @@ void AddTestData(bool forConsole, bool runOutsideProjectDirectory) [Theory] [MemberData(nameof(TestDataForAppBundleDir))] public async Task RunWithDifferentAppBundleLocations(bool forConsole, bool runOutsideProjectDirectory, string extraProperties) - => await (forConsole - ? ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory) - : BrowserRunTwiceWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory)); + => await (forConsole, ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory); + // [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] + //: BrowserRunTwiceWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory)); private async Task BrowserRunTwiceWithAndThenWithoutBuildAsync(string config, string extraProperties = "", bool runOutsideProjectDirectory = false) { @@ -416,6 +416,7 @@ public void ConsolePublishAndRun(string config, bool aot, bool relinking) // [ActiveIssue("https://github.com/dotnet/runtime/issues/90979")] // [InlineData("", BuildTestBase.DefaultTargetFramework, "./")] // [InlineData("-f net8.0", "net8.0", "./")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task BrowserBuildAndRun(string extraNewArgs, string targetFramework, string runtimeAssetsRelativePath) { string config = "Debug"; diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs index 96f2c4ebd6a1bf..aa39b1a376bc8a 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppSettingsTests.cs @@ -23,6 +23,7 @@ public AppSettingsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture [Theory] [InlineData("Development")] [InlineData("Production")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task LoadAppSettingsBasedOnApplicationEnvironment(string applicationEnvironment) { CopyTestAsset("WasmBasicTestApp", "AppSettingsTests"); diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs index d2219e9f9e50da..ec73cf68361ec4 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LazyLoadingTests.cs @@ -21,6 +21,7 @@ public LazyLoadingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task LoadLazyAssemblyBeforeItIsNeeded() { CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests"); @@ -31,6 +32,7 @@ public async Task LoadLazyAssemblyBeforeItIsNeeded() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task FailOnMissingLazyAssembly() { CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests"); diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs index 6f68a96ad1d61a..2227c05f19498a 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/LibraryInitializerTests.cs @@ -24,6 +24,7 @@ public LibraryInitializerTests(ITestOutputHelper output, SharedBuildPerTestClass } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task LoadLibraryInitializer() { CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_LoadLibraryInitializer"); @@ -37,6 +38,7 @@ public async Task LoadLibraryInitializer() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task AbortStartupOnError() { CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_AbortStartupOnError"); diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs index 31dcb65582869e..b82fae815234ec 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/SatelliteLoadingTests.cs @@ -24,6 +24,7 @@ public SatelliteLoadingTests(ITestOutputHelper output, SharedBuildPerTestClassFi } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task LoadSatelliteAssembly() { CopyTestAsset("WasmBasicTestApp", "SatelliteLoadingTests"); From 070114392428344cb91a72419b1d01c6af3ca807 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 16 Jan 2024 18:31:58 -0500 Subject: [PATCH 067/189] fix build --- src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs index 98d0331bb620be..6704fb04058dad 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs @@ -254,7 +254,7 @@ void AddTestData(bool forConsole, bool runOutsideProjectDirectory) [Theory] [MemberData(nameof(TestDataForAppBundleDir))] public async Task RunWithDifferentAppBundleLocations(bool forConsole, bool runOutsideProjectDirectory, string extraProperties) - => await (forConsole, ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory); + => await ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory); // [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] //: BrowserRunTwiceWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory)); From f065ad967139c07aca3cf43643190ca568f39340 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 16 Jan 2024 19:20:44 -0500 Subject: [PATCH 068/189] fix build --- .../wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs index 6704fb04058dad..322210d37d4230 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs @@ -254,9 +254,12 @@ void AddTestData(bool forConsole, bool runOutsideProjectDirectory) [Theory] [MemberData(nameof(TestDataForAppBundleDir))] public async Task RunWithDifferentAppBundleLocations(bool forConsole, bool runOutsideProjectDirectory, string extraProperties) - => await ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory); + { + _ = forConsole; + await ConsoleRunWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory); // [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] //: BrowserRunTwiceWithAndThenWithoutBuildAsync("Release", extraProperties, runOutsideProjectDirectory)); + } private async Task BrowserRunTwiceWithAndThenWithoutBuildAsync(string config, string extraProperties = "", bool runOutsideProjectDirectory = false) { From 275359e1938cf6daca45308b88382ba028959234 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 16 Jan 2024 21:51:23 -0500 Subject: [PATCH 069/189] disable some missed tests --- src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs | 1 + .../TestAppScenarios/DownloadResourceProgressTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs index 74d1fdf3f35e2a..1d18022fd25cf0 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs @@ -25,6 +25,7 @@ public SimpleRunTests(ITestOutputHelper output, SharedBuildPerTestClassFixture b [Theory] [InlineData("Debug")] [InlineData("Release")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task BlazorBuildRunTest(string config) { string id = $"blazor_{config}_{GetRandomId()}"; diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs index 70f9b4f1507d28..5088a2e6a1160e 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs @@ -23,6 +23,7 @@ public DownloadResourceProgressTests(ITestOutputHelper output, SharedBuildPerTes [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97054")] public async Task DownloadProgressFinishes(bool failAssemblyDownload) { CopyTestAsset("WasmBasicTestApp", $"DownloadResourceProgressTests_{failAssemblyDownload}"); From af874399a0de79c9364f3f0e8cebbd0c60e16dfa Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:12:28 +0100 Subject: [PATCH 070/189] [main] Update dependencies from dotnet/emsdk (#96676) * Update dependencies from https://github.com/dotnet/emsdk build 20240108.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24058.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24054.2 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240108.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24058.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24054.2 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240110.6 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24060.6 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24054.2 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240111.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24061.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24060.4 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240111.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24061.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24060.4 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240111.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24061.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24060.4 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240111.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24061.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24060.4 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240115.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24053.1 -> To Version 9.0.0-alpha.1.24065.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23618.1 -> To Version 16.0.5-alpha.1.24060.4 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 128 ++++++++++++++++++++-------------------- eng/Versions.props | 64 ++++++++++---------- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4773294f452def..13ba632f6d60d9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -12,73 +12,73 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/emsdk - 5cda86493ac07dce11dcb04323d2b57eecff00b7 + c14a1d2af9d67eec272ff7d7f3c5bb6b114798fe - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de https://github.com/dotnet/command-line-api @@ -94,9 +94,9 @@ 81facb3f6009be2cdce70df30452bb75e9a8f993 - + https://github.com/dotnet/emsdk - 5cda86493ac07dce11dcb04323d2b57eecff00b7 + c14a1d2af9d67eec272ff7d7f3c5bb6b114798fe @@ -240,61 +240,61 @@ https://github.com/dotnet/runtime-assets 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/llvm-project - cd4f3b5a9cf53c99bee84bed690f2b87fe275d60 + d8bacb4031b1d1e290ab2792e8378560419ee0de https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 05b80956836830..91aa5ff8cba9ad 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -106,14 +106,14 @@ 9.0.0-alpha.1.23617.5 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 6.0.0 1.1.1 @@ -213,39 +213,39 @@ 2.2.3 9.0.0-alpha.1.23605.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 - 9.0.0-alpha.1.24053.1 + 9.0.0-alpha.1.24065.1 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) - 9.0.0-alpha.1.24053.1 + 9.0.0-alpha.1.24065.1 1.1.87-gba258badda 1.0.0-v3.14.0.5722 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 - 16.0.5-alpha.1.23618.1 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24060.4 3.1.7 1.0.406601 From 6c9fbb401611cb6f82f2af25ae75c20ef2f0dd7a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:28:57 +0100 Subject: [PATCH 071/189] [main] Update dependencies from dotnet/roslyn (#96445) * Update dependencies from https://github.com/dotnet/roslyn build 20240102.3 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24052.3 * Update dependencies from https://github.com/dotnet/roslyn build 20240103.1 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24053.1 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 * Update dependencies from https://github.com/dotnet/roslyn build 20240104.13 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.9.0-3.23629.3 -> To Version 4.9.0-3.24054.13 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 13ba632f6d60d9..ce1513c9b772eb 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -361,18 +361,18 @@ https://github.com/dotnet/runtime-assets 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f - + https://github.com/dotnet/roslyn - ebb588725e707db23d8723b633258e7eb918277b + 28e49407a6e4744819bd471707259b99964e441c - + https://github.com/dotnet/roslyn - ebb588725e707db23d8723b633258e7eb918277b + 28e49407a6e4744819bd471707259b99964e441c - + https://github.com/dotnet/roslyn - ebb588725e707db23d8723b633258e7eb918277b + 28e49407a6e4744819bd471707259b99964e441c https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index 91aa5ff8cba9ad..571d0bfcaa164b 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -39,9 +39,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.9.0-3.23629.3 - 4.9.0-3.23629.3 - 4.9.0-3.23629.3 + 4.9.0-3.24054.13 + 4.9.0-3.24054.13 + 4.9.0-3.24054.13 + + net9.0 + Library + true + + From 1a985d9d113b3873f337cead6c3a136e1876cd2c Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 17 Jan 2024 15:34:24 +0100 Subject: [PATCH 073/189] [NativeAOT] 32-bit platform ObjWriter and bit rot fixes (#96890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use PC-relative relocations for ARMEmitter.EmitMOV(dest, symbol) * Fix handling of signed offsets in IMAGE_REL_BASED_THUMB_BRANCH24 calculations * Generate position independent code in NativeAOT on linux-arm * Handle ARM32 in DwarfCie * Fix relocations emitted in 32-bit ELF for x86/arm32; apply correct addends to account for R2R ABI differences; use inline addend for 32-bit platforms; mark function symbols as Thumb code on ARM * ELF/ARM32: Emit thumb mapping symbols for executable sections * Try to revert ARMEmitter.EmitMOV * Convert IMAGE_REL_BASED_THUMB_MOV32_PCREL to ELF with the same offset as R2R * Unoptimal, but working, version of INLINE_GET_TLS_VAR for ARM32 * Use PC-relative relocations for ARMEmitter.EmitMOV(dest, symbol) * Fat function pointers are not function symbols as far as ELF is concerned; the should not get the symbol size or the Thumb bit * Fix some bits and pieces of the ARM unwinding code * Don't try to use ObjWriter package on unsupported platforms * Generate valid ARM32 DWARF unwind info in JIT * Handle negative offsets in CFI_REL_OFFSET conversion (unused at the moment) * Add linux-arm support to cross-compile targets * Update src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc Co-authored-by: Jan Kotas * Update ObjectWriter.cs Co-authored-by: Michal Strehovský * Fix the order of register push in CFI unwind codes on ARM. * Make jit-format happy without making the code look ugly --------- Co-authored-by: Jan Kotas Co-authored-by: Michal Strehovský --- src/coreclr/jit/unwind.cpp | 33 ++--- src/coreclr/jit/unwindarmarch.cpp | 4 - .../Microsoft.NETCore.Native.Unix.targets | 6 +- .../Runtime/unix/UnixNativeCodeManager.cpp | 4 - .../nativeaot/Runtime/unix/UnwindHelpers.cpp | 140 ++++++++++-------- .../Runtime/unix/unixasmmacrosarm.inc | 17 ++- .../DependencyAnalysis/ObjectDataBuilder.cs | 1 + .../Compiler/DependencyAnalysis/Relocation.cs | 20 +-- .../Target_ARM/ARMEmitter.cs | 11 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 - .../Compiler/ObjectWriter/Dwarf/DwarfCie.cs | 13 ++ .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 12 +- .../Compiler/ObjectWriter/ElfNative.cs | 2 +- .../Compiler/ObjectWriter/ElfObjectWriter.cs | 97 +++++++----- .../Compiler/ObjectWriter/ObjectWriter.cs | 2 +- .../tools/aot/ILCompiler/ILCompiler.props | 2 +- 16 files changed, 210 insertions(+), 156 deletions(-) diff --git a/src/coreclr/jit/unwind.cpp b/src/coreclr/jit/unwind.cpp index e035b1188a22f8..7151aeaa8638b1 100644 --- a/src/coreclr/jit/unwind.cpp +++ b/src/coreclr/jit/unwind.cpp @@ -165,17 +165,11 @@ void Compiler::unwindPushPopCFI(regNumber reg) #endif ; + createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES); if (relOffsetMask & genRegMask(reg)) { -#ifndef TARGET_ARM - createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES); -#endif createCfiCode(func, cbProlog, CFI_REL_OFFSET, mapRegNumToDwarfReg(reg)); } - else - { - createCfiCode(func, cbProlog, CFI_ADJUST_CFA_OFFSET, DWARF_REG_ILLEGAL, REGSIZE_BYTES); - } } typedef jitstd::vector CFICodeVector; @@ -203,19 +197,20 @@ void Compiler::unwindBegPrologCFI() void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat) { - regMaskTP regBit = isFloat ? genRegMask(REG_FP_FIRST) : 1; +#if TARGET_ARM + regNumber regNum = isFloat ? REG_PREV(REG_FP_LAST) : REG_INT_LAST; + regMaskTP regBit = isFloat ? genRegMask(regNum) | genRegMask(REG_NEXT(regNum)) : genRegMask(regNum); +#else + regNumber regNum = isFloat ? REG_FP_LAST : REG_INT_LAST; + regMaskTP regBit = genRegMask(regNum); +#endif - regNumber regNum = isFloat ? REG_FP_FIRST : REG_FIRST; - for (; regNum < REG_COUNT;) + for (; regMask != 0 && regBit != RBM_NONE;) { - if (regBit > regMask) - { - break; - } - if (regBit & regMask) { unwindPushPopCFI(regNum); + regMask &= ~regBit; } #if TARGET_ARM @@ -224,11 +219,11 @@ void Compiler::unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat) // because LLVM only know about D0-D31. // As such pairs Sx,Sx+1 are referenced as D0-D15 registers in DWARF // For that we process registers in pairs. - regNum = isFloat ? REG_NEXT(REG_NEXT(regNum)) : REG_NEXT(regNum); - regBit <<= isFloat ? 2 : 1; + regBit >>= isFloat ? 2 : 1; + regNum = isFloat ? REG_PREV(REG_PREV(regNum)) : REG_PREV(regNum); #else - regNum = REG_NEXT(regNum); - regBit <<= 1; + regBit >>= 1; + regNum = REG_PREV(regNum); #endif } } diff --git a/src/coreclr/jit/unwindarmarch.cpp b/src/coreclr/jit/unwindarmarch.cpp index e53917ffda9c22..445b2581ca0abb 100644 --- a/src/coreclr/jit/unwindarmarch.cpp +++ b/src/coreclr/jit/unwindarmarch.cpp @@ -329,10 +329,6 @@ void Compiler::unwindPushMaskInt(regMaskTP maskInt) if (generateCFIUnwindCodes()) { // If we are pushing LR, we should give unwind codes in terms of caller's PC - if (maskInt & RBM_LR) - { - maskInt = (maskInt & ~RBM_LR) | RBM_PC; - } unwindPushPopMaskCFI(maskInt, false); return; } diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index ec91635d8fb1d3..19485ec753df50 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -39,9 +39,13 @@ The .NET Foundation licenses this file to you under the MIT license. x86_64 aarch64 arm64 + arm + + gnu + gnueabihf - $(CrossCompileArch)-linux-gnu + $(CrossCompileArch)-linux-$(CrossCompileAbi) $(CrossCompileArch)-alpine-linux-musl $(CrossCompileArch)-linux-android21 $(CrossCompileArch)-unknown-freebsd12 diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 68f58f77b4119e..9407499e329ca1 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -99,10 +99,6 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, pMethodInfo->unwind_info = procInfo.unwind_info; uintptr_t lsda = procInfo.lsda; -#if defined(HOST_ARM) - // libunwind fills by reference not by value for ARM - lsda = *((uintptr_t *)lsda); -#endif PTR_UInt8 p = dac_cast(lsda); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 120cb68d1811fa..6fd0df0c63546f 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -339,136 +339,155 @@ struct Registers_REGDISPLAY : REGDISPLAY #endif // TARGET_X86 #if defined(TARGET_ARM) -class Registers_arm_rt: public libunwind::Registers_arm { -public: - Registers_arm_rt() { abort(); }; - Registers_arm_rt(void *registers) { regs = (REGDISPLAY *)registers; }; - uint32_t getRegister(int num); +struct Registers_REGDISPLAY : REGDISPLAY +{ + inline static int getArch() { return libunwind::REGISTERS_ARM; } + inline static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; } + + bool validRegister(int num) const; + bool validFloatRegister(int num) { return false; }; + bool validVectorRegister(int num) const { return false; }; + + uint32_t getRegister(int num) const; void setRegister(int num, uint32_t value, uint32_t location); - uint32_t getRegisterLocation(int regNum) const { abort();} - unw_fpreg_t getFloatRegister(int num) { abort();} - void setFloatRegister(int num, unw_fpreg_t value) {abort();} - bool validVectorRegister(int num) const { abort();} - uint32_t getVectorRegister(int num) const {abort();}; - void setVectorRegister(int num, uint32_t value) {abort();}; - void jumpto() { abort();}; - uint32_t getSP() const { return regs->SP;} - void setSP(uint32_t value, uint32_t location) { regs->SP = value;} - uint32_t getIP() const { return regs->IP;} + + double getFloatRegister(int num) const {abort();} + void setFloatRegister(int num, double value) {abort();} + + libunwind::v128 getVectorRegister(int num) const {abort();} + void setVectorRegister(int num, libunwind::v128 value) {abort();} + + uint32_t getSP() const { return SP;} + void setSP(uint32_t value, uint32_t location) { SP = value;} + uint32_t getIP() const { return IP;} void setIP(uint32_t value, uint32_t location) - { regs->IP = value; regs->pIP = (PTR_UIntNative)location; } - void saveVFPAsX() {abort();}; -private: - REGDISPLAY *regs; + { IP = value; pIP = (PTR_UIntNative)location; } + uint32_t getFP() const { return *pR11;} + void setFP(uint32_t value, uint32_t location) { pR11 = (PTR_UIntNative)location;} }; -inline uint32_t Registers_arm_rt::getRegister(int regNum) { +inline bool Registers_REGDISPLAY::validRegister(int num) const { + if (num == UNW_REG_SP || num == UNW_ARM_SP) + return true; + + if (num == UNW_ARM_LR) + return true; + + if (num == UNW_REG_IP || num == UNW_ARM_IP) + return true; + + if (num >= UNW_ARM_R0 && num <= UNW_ARM_R12) + return true; + + return false; +} + +inline uint32_t Registers_REGDISPLAY::getRegister(int regNum) const { if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) - return regs->SP; + return SP; if (regNum == UNW_ARM_LR) - return *regs->pLR; + return *pLR; if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) - return regs->IP; + return IP; switch (regNum) { case (UNW_ARM_R0): - return *regs->pR0; + return *pR0; case (UNW_ARM_R1): - return *regs->pR1; + return *pR1; case (UNW_ARM_R2): - return *regs->pR2; + return *pR2; case (UNW_ARM_R3): - return *regs->pR3; + return *pR3; case (UNW_ARM_R4): - return *regs->pR4; + return *pR4; case (UNW_ARM_R5): - return *regs->pR5; + return *pR5; case (UNW_ARM_R6): - return *regs->pR6; + return *pR6; case (UNW_ARM_R7): - return *regs->pR7; + return *pR7; case (UNW_ARM_R8): - return *regs->pR8; + return *pR8; case (UNW_ARM_R9): - return *regs->pR9; + return *pR9; case (UNW_ARM_R10): - return *regs->pR10; + return *pR10; case (UNW_ARM_R11): - return *regs->pR11; + return *pR11; case (UNW_ARM_R12): - return *regs->pR12; + return *pR12; } PORTABILITY_ASSERT("unsupported arm register"); } -void Registers_arm_rt::setRegister(int num, uint32_t value, uint32_t location) +void Registers_REGDISPLAY::setRegister(int num, uint32_t value, uint32_t location) { - if (num == UNW_REG_SP || num == UNW_ARM_SP) { - regs->SP = (uintptr_t )value; + SP = (uintptr_t )value; return; } if (num == UNW_ARM_LR) { - regs->pLR = (PTR_UIntNative)location; + pLR = (PTR_UIntNative)location; return; } if (num == UNW_REG_IP || num == UNW_ARM_IP) { - regs->IP = value; + IP = value; /* the location could be NULL, we could try to recover pointer to value in stack from pLR */ - if ((!location) && (regs->pLR) && (*regs->pLR == value)) - regs->pIP = regs->pLR; + if ((!location) && pLR && (*pLR == value)) + pIP = pLR; else - regs->pIP = (PTR_UIntNative)location; + pIP = (PTR_UIntNative)location; return; } switch (num) { case (UNW_ARM_R0): - regs->pR0 = (PTR_UIntNative)location; + pR0 = (PTR_UIntNative)location; break; case (UNW_ARM_R1): - regs->pR1 = (PTR_UIntNative)location; + pR1 = (PTR_UIntNative)location; break; case (UNW_ARM_R2): - regs->pR2 = (PTR_UIntNative)location; + pR2 = (PTR_UIntNative)location; break; case (UNW_ARM_R3): - regs->pR3 = (PTR_UIntNative)location; + pR3 = (PTR_UIntNative)location; break; case (UNW_ARM_R4): - regs->pR4 = (PTR_UIntNative)location; + pR4 = (PTR_UIntNative)location; break; case (UNW_ARM_R5): - regs->pR5 = (PTR_UIntNative)location; + pR5 = (PTR_UIntNative)location; break; case (UNW_ARM_R6): - regs->pR6 = (PTR_UIntNative)location; + pR6 = (PTR_UIntNative)location; break; case (UNW_ARM_R7): - regs->pR7 = (PTR_UIntNative)location; + pR7 = (PTR_UIntNative)location; break; case (UNW_ARM_R8): - regs->pR8 = (PTR_UIntNative)location; + pR8 = (PTR_UIntNative)location; break; case (UNW_ARM_R9): - regs->pR9 = (PTR_UIntNative)location; + pR9 = (PTR_UIntNative)location; break; case (UNW_ARM_R10): - regs->pR10 = (PTR_UIntNative)location; + pR10 = (PTR_UIntNative)location; break; case (UNW_ARM_R11): - regs->pR11 = (PTR_UIntNative)location; + pR11 = (PTR_UIntNative)location; break; case (UNW_ARM_R12): - regs->pR12 = (PTR_UIntNative)location; + pR12 = (PTR_UIntNative)location; break; default: PORTABILITY_ASSERT("unsupported arm register"); @@ -788,13 +807,8 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t fo uintptr_t pc = regs->GetIP(); bool isSignalFrame = false; -#if defined(TARGET_ARM) - DwarfInstructions dwarfInst; - int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, unwind_info, *(Registers_arm_rt*)regs, isSignalFrame, /* stage2 */ false); -#else DwarfInstructions dwarfInst; int stepRet = dwarfInst.stepWithDwarf(_addressSpace, pc, unwind_info, *(Registers_REGDISPLAY*)regs, isSignalFrame, /* stage2 */ false); -#endif if (stepRet != UNW_STEP_SUCCESS) { @@ -819,7 +833,7 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio #if defined(TARGET_AMD64) libunwind::UnwindCursor uc(_addressSpace); #elif defined(TARGET_ARM) - libunwind::UnwindCursor uc(_addressSpace); + libunwind::UnwindCursor uc(_addressSpace); #elif defined(TARGET_ARM64) libunwind::UnwindCursor uc(_addressSpace); #elif defined(HOST_X86) diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc index fbfc6c84ff098d..9f1d17a46188ae 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc @@ -259,13 +259,18 @@ C_FUNC(\Name): ldr r0, 2f 1: add r0, pc, r0 - bl __tls_get_addr(PLT) - // push data at the end of text section - .pushsection .text.tlsvar, "aM", %progbits, 4 - .balign 4 + bl __tls_get_addr + b 3f + + // Inline data + // LLVM assembler has no concept of subsections and this is not expressible as + // cross-section relocation. + .p2align 2 2: - .4byte \Var@TLSGD + (. - 1b - 4) - .popsection + .extern \Var + .type \Var, tls_object + .long \Var(TLSGD) + (2b - 1b - 4) +3: .endm .macro INLINE_GETTHREAD diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index 19f367e82518cc..1181b642a7df77 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -306,6 +306,7 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: case RelocType.IMAGE_REL_BASED_THUMB_MOV32: + case RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL: case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L: case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index bbe1ce26784b8e..337811103e35f7 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -154,13 +154,13 @@ private static unsafe int GetThumb2BlRel24(ushort* p) ((Opcode1 << 1) & 0x0000FFE); // Sign-extend and return - return (int)((ret << 7) >> 7); + return (int)(ret << 7) >> 7; } //***************************************************************************** // Returns whether the offset fits into bl instruction //***************************************************************************** - private static bool FitsInThumb2BlRel24(uint imm24) + private static bool FitsInThumb2BlRel24(int imm24) { return ((imm24 << 7) >> 7) == imm24; } @@ -168,7 +168,7 @@ private static bool FitsInThumb2BlRel24(uint imm24) //***************************************************************************** // Deposit the 24-bit rel offset into bl instruction //***************************************************************************** - private static unsafe void PutThumb2BlRel24(ushort* p, uint imm24) + private static unsafe void PutThumb2BlRel24(ushort* p, int imm24) { // Verify that we got a valid offset Debug.Assert(FitsInThumb2BlRel24(imm24)); @@ -182,17 +182,17 @@ private static unsafe void PutThumb2BlRel24(ushort* p, uint imm24) Opcode0 &= 0xF800; Opcode1 &= 0xD000; - uint S = (imm24 & 0x1000000) >> 24; - uint J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1; - uint J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1; + uint S = ((uint)imm24 & 0x1000000) >> 24; + uint J1 = (((uint)imm24 & 0x0800000) >> 23) ^ S ^ 1; + uint J2 = (((uint)imm24 & 0x0400000) >> 22) ^ S ^ 1; - Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10); - Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11); + Opcode0 |= (((uint)imm24 & 0x03FF000) >> 12) | (S << 10); + Opcode1 |= (((uint)imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11); p[0] = (ushort)Opcode0; p[1] = (ushort)Opcode1; - Debug.Assert((uint)GetThumb2BlRel24(p) == imm24); + Debug.Assert(GetThumb2BlRel24(p) == imm24); } //***************************************************************************** @@ -485,7 +485,7 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v PutThumb2Mov32((ushort*)location, (uint)value); break; case RelocType.IMAGE_REL_BASED_THUMB_BRANCH24: - PutThumb2BlRel24((ushort*)location, (uint)value); + PutThumb2BlRel24((ushort*)location, (int)value); break; case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: PutArm64Rel28((uint*)location, value); diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs index dd41f891552a1f..822a351610b9ba 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs @@ -139,15 +139,24 @@ public void EmitLDR(Register destination, Register source, int offset) // movw reg, [reloc] & 0x0000FFFF // movt reg, [reloc] & 0xFFFF0000 + // add reg, pc // reg range: [0..12, LR] public void EmitMOV(Register destination, ISymbolNode symbol) { Debug.Assert(destination >= Register.R0 && (destination <= Register.R12 || destination == TargetRegister.LR)); - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_THUMB_MOV32); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL); // 12-byte offset is part of the relocation Builder.EmitShort(unchecked((short)0xf240)); Builder.EmitShort((short)((byte)destination << 8)); Builder.EmitShort(unchecked((short)0xf2c0)); Builder.EmitShort((short)((byte)destination << 8)); + if (destination <= Register.R7) + { + Builder.EmitShort(unchecked((short)(0x4478u + (byte)destination))); + } + else + { + Builder.EmitShort(unchecked((short)(0x44f0u + (byte)destination))); + } } // b.w symbol diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 19faf31a208674..cadc58a7014516 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4081,10 +4081,8 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) break; } -#if READYTORUN if (targetArchitecture == TargetArchitecture.ARM && !_compilation.TypeSystemContext.Target.IsWindows) flags.Set(CorJitFlag.CORJIT_FLAG_RELATIVE_CODE_RELOCS); -#endif if (this.MethodBeingCompiled.IsUnmanagedCallersOnly) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs index f0bc413e2c6e70..7497734283314d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs @@ -39,6 +39,19 @@ public DwarfCie(TargetArchitecture targetArchitecture) switch (targetArchitecture) { + case TargetArchitecture.ARM: + CodeAlignFactor = 1; + DataAlignFactor = -4; + ReturnAddressRegister = 14; // LR + Instructions = new byte[] + { + DW_CFA_def_cfa, + 13, // SP + 0, // Offset from SP + }; + InitialCFAOffset = 0; + break; + case TargetArchitecture.ARM64: CodeAlignFactor = 1; DataAlignFactor = -4; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs index ea3bcdc740ec0d..b3ea6ac201286c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs @@ -89,16 +89,24 @@ private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) break; case CFI_OPCODE.CFI_REL_OFFSET: - if (dwarfReg <= 0x3F) + int absOffset = ((cfiOffset - cfaOffset) / cie.DataAlignFactor); + if (absOffset < 0) + { + cfiCode[cfiCodeOffset++] = DW_CFA_offset_extended_sf; + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)dwarfReg); + cfiCodeOffset += DwarfHelper.WriteSLEB128(cfiCode.AsSpan(cfiCodeOffset), absOffset); + } + else if (dwarfReg <= 0x3F) { cfiCode[cfiCodeOffset++] = (byte)(DW_CFA_offset | (byte)dwarfReg); + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)absOffset); } else { cfiCode[cfiCodeOffset++] = DW_CFA_offset_extended; cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)dwarfReg); + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)absOffset); } - cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)((cfiOffset - cfaOffset) / cie.DataAlignFactor)); break; case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs index b39253a02e8545..625bfabef36fe4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs @@ -182,7 +182,7 @@ internal static class ElfNative public const uint R_ARM_THM_ABS5 = 7; public const uint R_ARM_ABS8 = 8; public const uint R_ARM_SBREL32 = 9; - public const uint R_ARM_THM_PC22 = 10; + public const uint R_ARM_THM_CALL = 10; public const uint R_ARM_THM_PC8 = 11; public const uint R_ARM_AMP_VCALL9 = 12; public const uint R_ARM_SWI24 = 13; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs index 00648d5d7b897a..17fa66b7c7a3f6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -32,6 +32,7 @@ namespace ILCompiler.ObjectWriter /// internal sealed class ElfObjectWriter : UnixObjectWriter { + private readonly bool _useInlineRelocationAddends; private readonly ushort _machine; private readonly List _sections = new(); private readonly List _symbols = new(); @@ -52,6 +53,7 @@ public ElfObjectWriter(NodeFactory factory, ObjectWritingOptions options) TargetArchitecture.ARM64 => EM_AARCH64, _ => throw new NotSupportedException("Unsupported architecture") }; + _useInlineRelocationAddends = _machine is EM_386 or EM_ARM; // By convention the symbol table starts with empty symbol _symbols.Add(new ElfSymbol {}); @@ -138,6 +140,16 @@ private protected override void CreateSection(ObjectNodeSection section, string }); } + if (_machine is EM_ARM && section.Type is SectionType.Executable) + { + _symbols.Add(new ElfSymbol + { + Section = _sections[sectionIndex], + Info = STT_NOTYPE, + Name = $"$t.{sectionIndex}" + }); + } + base.CreateSection(section, comdatName, symbolName ?? sectionName, sectionStream); } @@ -155,16 +167,41 @@ protected internal override unsafe void EmitRelocation( string symbolName, long addend) { - // We read the addend from the data and clear it. This is necessary - // to produce correct addends in the `.rela` sections which override - // the destination with the addend from relocation table. fixed (byte *pData = data) { - long inlineValue = Relocation.ReadValue(relocType, (void*)pData); - if (inlineValue != 0) + if (relocType is IMAGE_REL_BASED_REL32 && _machine is EM_386 or EM_X86_64) + { + addend -= 4; + } + else if (relocType is IMAGE_REL_BASED_THUMB_BRANCH24) + { + addend -= 4; + } + else if (relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL) + { + addend -= 12; + } + + if (!_useInlineRelocationAddends) { - addend += inlineValue; - Relocation.WriteValue(relocType, (void*)pData, 0); + // We read the addend from the data and clear it. This is necessary + // to produce correct addends in the `.rela` sections which override + // the destination with the addend from relocation table. + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + if (inlineValue != 0) + { + addend += inlineValue; + Relocation.WriteValue(relocType, (void*)pData, 0); + } + } + else + { + if (addend != 0) + { + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + Relocation.WriteValue(relocType, (void*)pData, inlineValue + addend); + addend = 0; + } } } @@ -182,10 +219,11 @@ private protected override void EmitSymbolTable( var type = (section.SectionHeader.Flags & SHF_TLS) == SHF_TLS ? STT_TLS : definition.Size > 0 ? STT_FUNC : STT_NOTYPE; + ulong thumbBit = _machine == EM_ARM && type == STT_FUNC ? 1u : 0u; sortedSymbols.Add(new ElfSymbol { Name = name, - Value = (ulong)definition.Value, + Value = (ulong)definition.Value | thumbBit, Size = (ulong)definition.Size, Section = _sections[definition.SectionIndex], Info = (byte)(type | (STB_GLOBAL << 4)), @@ -246,13 +284,10 @@ private protected override void EmitRelocations(int sectionIndex, List relocationList) { - // TODO: We are emitting .rela sections on x86 which is technically wrong. We should be - // using .rel sections with the addend embedded in the data. Since x86 is not an officially - // supported platform this is left for future enhancement. if (relocationList.Count > 0) { - Span relocationEntry = stackalloc byte[12]; - var relocationStream = new MemoryStream(12 * relocationList.Count); + Span relocationEntry = stackalloc byte[8]; + var relocationStream = new MemoryStream(8 * relocationList.Count); _sections[sectionIndex].RelocationStream = relocationStream; foreach (SymbolicRelocation symbolicRelocation in relocationList) { @@ -267,15 +302,8 @@ private void EmitRelocationsX86(int sectionIndex, List reloc _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; - long addend = symbolicRelocation.Addend; - if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) - { - addend -= 4; - } - BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)symbolicRelocation.Offset); BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | type); - BinaryPrimitives.WriteInt32LittleEndian(relocationEntry.Slice(8), (int)addend); relocationStream.Write(relocationEntry); } } @@ -302,15 +330,9 @@ private void EmitRelocationsX64(int sectionIndex, List reloc _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; - long addend = symbolicRelocation.Addend; - if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) - { - addend -= 4; - } - BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)symbolicRelocation.Offset); BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); - BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), addend); + BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); relocationStream.Write(relocationEntry); } } @@ -320,8 +342,8 @@ private void EmitRelocationsARM(int sectionIndex, List reloc { if (relocationList.Count > 0) { - Span relocationEntry = stackalloc byte[12]; - var relocationStream = new MemoryStream(12 * relocationList.Count); + Span relocationEntry = stackalloc byte[8]; + var relocationStream = new MemoryStream(8 * relocationList.Count); _sections[sectionIndex].RelocationStream = relocationStream; foreach (SymbolicRelocation symbolicRelocation in relocationList) { @@ -333,19 +355,12 @@ private void EmitRelocationsARM(int sectionIndex, List reloc IMAGE_REL_BASED_REL32 => R_ARM_REL32, IMAGE_REL_BASED_THUMB_MOV32 => R_ARM_THM_MOVW_ABS_NC, IMAGE_REL_BASED_THUMB_MOV32_PCREL => R_ARM_THM_MOVW_PREL_NC, - IMAGE_REL_BASED_THUMB_BRANCH24 => R_ARM_THM_PC22, + IMAGE_REL_BASED_THUMB_BRANCH24 => R_ARM_THM_CALL, _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; - long addend = symbolicRelocation.Addend; - if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) - { - addend -= 4; - } - BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)symbolicRelocation.Offset); BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | type); - BinaryPrimitives.WriteInt32LittleEndian(relocationEntry.Slice(8), (int)addend); relocationStream.Write(relocationEntry); if (symbolicRelocation.Type is IMAGE_REL_BASED_THUMB_MOV32 or IMAGE_REL_BASED_THUMB_MOV32_PCREL) @@ -461,7 +476,7 @@ private void EmitObjectFile(FileStream outputFileStream) if (section.RelocationStream != Stream.Null) { - _stringTable.ReserveString(".rela" + section.Name); + _stringTable.ReserveString((_useInlineRelocationAddends ? ".rel" : ".rela") + section.Name); sectionCount++; } @@ -582,8 +597,8 @@ private void EmitObjectFile(FileStream outputFileStream) { ElfSectionHeader relaSectionHeader = new ElfSectionHeader { - NameIndex = _stringTable.GetStringOffset(".rela" + section.Name), - Type = SHT_RELA, + NameIndex = _stringTable.GetStringOffset((_useInlineRelocationAddends ? ".rel" : ".rela") + section.Name), + Type = _useInlineRelocationAddends ? SHT_REL : SHT_RELA, Flags = (section.GroupSection is not null ? SHF_GROUP : 0u) | SHF_INFO_LINK, Address = 0u, Offset = section.SectionHeader.Offset + section.SectionHeader.Size, @@ -591,7 +606,7 @@ private void EmitObjectFile(FileStream outputFileStream) Link = symTabSectionIndex, Info = section.SectionIndex, Alignment = 8u, - EntrySize = (ulong)default(TSize).GetByteCount() * 3u, + EntrySize = (ulong)default(TSize).GetByteCount() * (_useInlineRelocationAddends ? 2u : 3u), }; relaSectionHeader.Write(outputFileStream); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs index 9699c9551a2f19..fde0bb8a53d01a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs @@ -388,7 +388,7 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection true - true + true From fe3a2b9e26ad9a2766d209ea6aea2e7b9c07eb4a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 17 Jan 2024 10:28:53 -0500 Subject: [PATCH 074/189] Reduce code size impact of `ArrayPool` (#97058) * Reduce code size impact of `ArrayPool` Every `SharedArrayPool` brings with it several types, including several array types and all of their interface implementations. This makes the internals of SharedArrayPool a bit less generic to try to reduce that impact. The changes are effectively: - Move the nested Partition, Partitions, and ThreadLocalArray types out from being nested types to being peers - Rename them, since they're no longer inheriting the parent's name - Make them non-generic in terms of Array rather than generic in terms of T[]. These types never index into the array, so other than accessing Length for logging purposes, it could even have used object. - Use Unsafe.As in the two places arrays are dequeued and need to be T[] rather than Array. - Pass in the sizeof(T) rather than using it in the implementation. The size is used only when trimming to determine how much to trim. * Fix throughput regression from covariance check --- .../src/System/Buffers/SharedArrayPool.cs | 227 +++++++++--------- 1 file changed, 119 insertions(+), 108 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs index bfa50207dc3df1..89bbd450452391 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/SharedArrayPool.cs @@ -26,21 +26,24 @@ internal sealed partial class SharedArrayPool : ArrayPool /// A per-thread array of arrays, to cache one array per array size per thread. [ThreadStatic] - private static ThreadLocalArray[]? t_tlsBuckets; + private static SharedArrayPoolThreadLocalArray[]? t_tlsBuckets; /// Used to keep track of all thread local buckets for trimming if needed. - private readonly ConditionalWeakTable _allTlsBuckets = new ConditionalWeakTable(); + private readonly ConditionalWeakTable _allTlsBuckets = new ConditionalWeakTable(); /// /// An array of per-core partitions. The slots are lazily initialized to avoid creating /// lots of overhead for unused array sizes. /// - private readonly Partitions?[] _buckets = new Partitions[NumBuckets]; + private readonly SharedArrayPoolPartitions?[] _buckets = new SharedArrayPoolPartitions[NumBuckets]; /// Whether the callback to trim arrays in response to memory pressure has been created. private int _trimCallbackCreated; - /// Allocate a new and try to store it into the array. - private Partitions CreatePerCorePartitions(int bucketIndex) + /// Allocate a new and try to store it into the array. + private unsafe SharedArrayPoolPartitions CreatePerCorePartitions(int bucketIndex) { - var inst = new Partitions(); +#pragma warning disable 8500 // sizeof of managed types + int elementSize = sizeof(T); +#pragma warning restore 8500 + var inst = new SharedArrayPoolPartitions(elementSize); return Interlocked.CompareExchange(ref _buckets[bucketIndex], inst, null) ?? inst; } @@ -57,10 +60,10 @@ public override T[] Rent(int minimumLength) int bucketIndex = Utilities.SelectBucketIndex(minimumLength); // First, try to get an array from TLS if possible. - ThreadLocalArray[]? tlsBuckets = t_tlsBuckets; + SharedArrayPoolThreadLocalArray[]? tlsBuckets = t_tlsBuckets; if (tlsBuckets is not null && (uint)bucketIndex < (uint)tlsBuckets.Length) { - buffer = tlsBuckets[bucketIndex].Array; + buffer = Unsafe.As(tlsBuckets[bucketIndex].Array); if (buffer is not null) { tlsBuckets[bucketIndex].Array = null; @@ -73,13 +76,13 @@ public override T[] Rent(int minimumLength) } // Next, try to get an array from one of the partitions. - Partitions?[] perCoreBuckets = _buckets; + SharedArrayPoolPartitions?[] perCoreBuckets = _buckets; if ((uint)bucketIndex < (uint)perCoreBuckets.Length) { - Partitions? b = perCoreBuckets[bucketIndex]; + SharedArrayPoolPartitions? b = perCoreBuckets[bucketIndex]; if (b is not null) { - buffer = b.TryPop(); + buffer = Unsafe.As(b.TryPop()); if (buffer is not null) { if (log.IsEnabled()) @@ -133,7 +136,7 @@ public override void Return(T[] array, bool clearArray = false) // this if the array being returned is erroneous or too large for the pool, but the // former condition is an error we don't need to optimize for, and the latter is incredibly // rare, given a max size of 1B elements. - ThreadLocalArray[] tlsBuckets = t_tlsBuckets ?? InitializeTlsBucketsAndTrimming(); + SharedArrayPoolThreadLocalArray[] tlsBuckets = t_tlsBuckets ?? InitializeTlsBucketsAndTrimming(); bool haveBucket = false; bool returned = true; @@ -156,12 +159,12 @@ public override void Return(T[] array, bool clearArray = false) // Store the array into the TLS bucket. If there's already an array in it, // push that array down into the partitions, preferring to keep the latest // one in TLS for better locality. - ref ThreadLocalArray tla = ref tlsBuckets[bucketIndex]; - T[]? prev = tla.Array; - tla = new ThreadLocalArray(array); + ref SharedArrayPoolThreadLocalArray tla = ref tlsBuckets[bucketIndex]; + Array? prev = tla.Array; + tla = new SharedArrayPoolThreadLocalArray(array); if (prev is not null) { - Partitions partitionsForArraySize = _buckets[bucketIndex] ?? CreatePerCorePartitions(bucketIndex); + SharedArrayPoolPartitions partitionsForArraySize = _buckets[bucketIndex] ?? CreatePerCorePartitions(bucketIndex); returned = partitionsForArraySize.TryPush(prev); } } @@ -193,7 +196,7 @@ public bool Trim() } // Trim each of the per-core buckets. - Partitions?[] perCoreBuckets = _buckets; + SharedArrayPoolPartitions?[] perCoreBuckets = _buckets; for (int i = 0; i < perCoreBuckets.Length; i++) { perCoreBuckets[i]?.Trim(currentMilliseconds, Id, pressure, Utilities.GetMaxSizeForBucket(i)); @@ -209,16 +212,16 @@ public bool Trim() { if (!log.IsEnabled()) { - foreach (KeyValuePair tlsBuckets in _allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets) { Array.Clear(tlsBuckets.Key); } } else { - foreach (KeyValuePair tlsBuckets in _allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets) { - ThreadLocalArray[] buckets = tlsBuckets.Key; + SharedArrayPoolThreadLocalArray[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) { if (Interlocked.Exchange(ref buckets[i].Array, null) is T[] buffer) @@ -241,9 +244,9 @@ public bool Trim() _ => 30_000, }; - foreach (KeyValuePair tlsBuckets in _allTlsBuckets) + foreach (KeyValuePair tlsBuckets in _allTlsBuckets) { - ThreadLocalArray[] buckets = tlsBuckets.Key; + SharedArrayPoolThreadLocalArray[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) { if (buckets[i].Array is null) @@ -275,11 +278,11 @@ public bool Trim() return true; } - private ThreadLocalArray[] InitializeTlsBucketsAndTrimming() + private SharedArrayPoolThreadLocalArray[] InitializeTlsBucketsAndTrimming() { Debug.Assert(t_tlsBuckets is null, $"Non-null {nameof(t_tlsBuckets)}"); - var tlsBuckets = new ThreadLocalArray[NumBuckets]; + var tlsBuckets = new SharedArrayPoolThreadLocalArray[NumBuckets]; t_tlsBuckets = tlsBuckets; _allTlsBuckets.Add(tlsBuckets, null); @@ -290,90 +293,115 @@ private ThreadLocalArray[] InitializeTlsBucketsAndTrimming() return tlsBuckets; } + } + + // The following partition types are separated out of SharedArrayPool to avoid + // them being generic, in order to avoid the generic code size increase that would + // result, in particular for Native AOT. The only thing that's necessary to actually + // be generic is the return type of TryPop, and we can handle that at the access + // site with a well-placed Unsafe.As. - /// Provides a collection of partitions, each of which is a pool of arrays. - private sealed class Partitions + /// Wrapper for arrays stored in ThreadStatic buckets. + internal struct SharedArrayPoolThreadLocalArray + { + /// The stored array. + public Array? Array; + /// Environment.TickCount timestamp for when this array was observed by Trim. + public int MillisecondsTimeStamp; + + public SharedArrayPoolThreadLocalArray(Array array) { - /// The partitions. - private readonly Partition[] _partitions; + Array = array; + MillisecondsTimeStamp = 0; + } + } + + /// Provides a collection of partitions, each of which is a pool of arrays. + internal sealed class SharedArrayPoolPartitions + { + /// The partitions. + private readonly Partition[] _partitions; - /// Initializes the partitions. - public Partitions() + /// Initializes the partitions. + /// The size of the elements stored in arrays. + public SharedArrayPoolPartitions(int elementSize) + { + // Create the partitions. We create as many as there are processors, limited by our max. + var partitions = new Partition[SharedArrayPoolStatics.s_partitionCount]; + for (int i = 0; i < partitions.Length; i++) { - // Create the partitions. We create as many as there are processors, limited by our max. - var partitions = new Partition[SharedArrayPoolStatics.s_partitionCount]; - for (int i = 0; i < partitions.Length; i++) - { - partitions[i] = new Partition(); - } - _partitions = partitions; + partitions[i] = new Partition(elementSize); } + _partitions = partitions; + } - /// - /// Try to push the array into any partition with available space, starting with partition associated with the current core. - /// If all partitions are full, the array will be dropped. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryPush(T[] array) + /// + /// Try to push the array into any partition with available space, starting with partition associated with the current core. + /// If all partitions are full, the array will be dropped. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPush(Array array) + { + // Try to push on to the associated partition first. If that fails, + // round-robin through the other partitions. + Partition[] partitions = _partitions; + int index = (int)((uint)Thread.GetCurrentProcessorId() % (uint)SharedArrayPoolStatics.s_partitionCount); // mod by constant in tier 1 + for (int i = 0; i < partitions.Length; i++) { - // Try to push on to the associated partition first. If that fails, - // round-robin through the other partitions. - Partition[] partitions = _partitions; - int index = (int)((uint)Thread.GetCurrentProcessorId() % (uint)SharedArrayPoolStatics.s_partitionCount); // mod by constant in tier 1 - for (int i = 0; i < partitions.Length; i++) - { - if (partitions[index].TryPush(array)) return true; - if (++index == partitions.Length) index = 0; - } - - return false; + if (partitions[index].TryPush(array)) return true; + if (++index == partitions.Length) index = 0; } - /// - /// Try to pop an array from any partition with available arrays, starting with partition associated with the current core. - /// If all partitions are empty, null is returned. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[]? TryPop() + return false; + } + + /// + /// Try to pop an array from any partition with available arrays, starting with partition associated with the current core. + /// If all partitions are empty, null is returned. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Array? TryPop() + { + // Try to pop from the associated partition first. If that fails, round-robin through the other partitions. + Array? arr; + Partition[] partitions = _partitions; + int index = (int)((uint)Thread.GetCurrentProcessorId() % (uint)SharedArrayPoolStatics.s_partitionCount); // mod by constant in tier 1 + for (int i = 0; i < partitions.Length; i++) { - // Try to pop from the associated partition first. If that fails, round-robin through the other partitions. - T[]? arr; - Partition[] partitions = _partitions; - int index = (int)((uint)Thread.GetCurrentProcessorId() % (uint)SharedArrayPoolStatics.s_partitionCount); // mod by constant in tier 1 - for (int i = 0; i < partitions.Length; i++) - { - if ((arr = partitions[index].TryPop()) is not null) return arr; - if (++index == partitions.Length) index = 0; - } - return null; + if ((arr = partitions[index].TryPop()) is not null) return arr; + if (++index == partitions.Length) index = 0; } + return null; + } - public void Trim(int currentMilliseconds, int id, Utilities.MemoryPressure pressure, int bucketSize) + public void Trim(int currentMilliseconds, int id, Utilities.MemoryPressure pressure, int bucketSize) + { + Partition[] partitions = _partitions; + for (int i = 0; i < partitions.Length; i++) { - Partition[] partitions = _partitions; - for (int i = 0; i < partitions.Length; i++) - { - partitions[i].Trim(currentMilliseconds, id, pressure, bucketSize); - } + partitions[i].Trim(currentMilliseconds, id, pressure, bucketSize); } } /// Provides a simple, bounded stack of arrays, protected by a lock. - private sealed class Partition + /// The size of the elements stored in arrays. + private sealed class Partition(int elementSize) { /// The arrays in the partition. - private readonly T[]?[] _arrays = new T[SharedArrayPoolStatics.s_maxArraysPerPartition][]; + private readonly Array?[] _arrays = new Array[SharedArrayPoolStatics.s_maxArraysPerPartition]; + /// The size of the elements stored in arrays. + private readonly int _elementSize = elementSize; /// Number of arrays stored in . private int _count; /// Timestamp set by Trim when it sees this as 0. private int _millisecondsTimestamp; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryPush(T[] array) + public bool TryPush(Array array) { bool enqueued = false; Monitor.Enter(this); - T[]?[] arrays = _arrays; + Array?[] arrays = _arrays; int count = _count; if ((uint)count < (uint)arrays.Length) { @@ -384,7 +412,7 @@ public bool TryPush(T[] array) _millisecondsTimestamp = 0; } - arrays[count] = array; + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(arrays), count) = array; // arrays[count] = array, but avoiding stelemref _count = count + 1; enqueued = true; } @@ -393,11 +421,11 @@ public bool TryPush(T[] array) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[]? TryPop() + public Array? TryPop() { - T[]? arr = null; + Array? arr = null; Monitor.Enter(this); - T[]?[] arrays = _arrays; + Array?[] arrays = _arrays; int count = _count - 1; if ((uint)count < (uint)arrays.Length) { @@ -463,19 +491,17 @@ public void Trim(int currentMilliseconds, int id, Utilities.MemoryPressure press { trimCount++; } - unsafe + + if (_elementSize > ModerateTypeSize) { -#pragma warning disable 8500 // sizeof of managed types - if (sizeof(T) > ModerateTypeSize) - { - trimCount++; - } - if (sizeof(T) > LargeTypeSize) + trimCount++; + + if (_elementSize > LargeTypeSize) { trimCount++; } -#pragma warning restore 8500 } + break; case Utilities.MemoryPressure.Medium: @@ -485,7 +511,7 @@ public void Trim(int currentMilliseconds, int id, Utilities.MemoryPressure press while (_count > 0 && trimCount-- > 0) { - T[]? array = _arrays[--_count]; + Array? array = _arrays[--_count]; Debug.Assert(array is not null, "No nulls should have been present in slots < _count."); _arrays[_count] = null; @@ -501,21 +527,6 @@ public void Trim(int currentMilliseconds, int id, Utilities.MemoryPressure press } } } - - /// Wrapper for arrays stored in ThreadStatic buckets. - private struct ThreadLocalArray - { - /// The stored array. - public T[]? Array; - /// Environment.TickCount timestamp for when this array was observed by Trim. - public int MillisecondsTimeStamp; - - public ThreadLocalArray(T[] array) - { - Array = array; - MillisecondsTimeStamp = 0; - } - } } internal static class SharedArrayPoolStatics From 05fe3e05b5b2e23faab63afbba1a87e25828e4c5 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 17 Jan 2024 10:50:33 -0500 Subject: [PATCH 075/189] JIT ARM64-SVE: Implement IF_SVE_DL_2A, IF_SVE_DZ_1A, IF_SVE_EA_1A (#97068) --- src/coreclr/jit/codegenarm64test.cpp | 33 ++++++ src/coreclr/jit/emitarm64.cpp | 149 +++++++++++++++++++++++++-- src/coreclr/jit/emitarm64.h | 11 +- 3 files changed, 180 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 3d0a85ff099e51..162cf751c37deb 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5181,6 +5181,24 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_I(INS_sve_uqrshrn, EA_SCALABLE, REG_V15, REG_V12, 1, INS_OPTS_SCALABLE_H); // UQRSHRN .H, {.S-.S }, # + // IF_SVE_DL_2A + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R0, REG_P0, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_VL_2X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R1, REG_P1, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_VL_4X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R2, REG_P2, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_VL_2X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R3, REG_P3, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_VL_4X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R4, REG_P4, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_VL_2X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R5, REG_P5, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_VL_4X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R6, REG_P6, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_VL_2X); // CNTP , ., + theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R7, REG_P7, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_VL_4X); // CNTP , ., + // IF_SVE_DM_2A theEmitter->emitIns_R_R(INS_sve_decp, EA_8BYTE, REG_R0, REG_P0, INS_OPTS_SCALABLE_B); // DECP , . theEmitter->emitIns_R_R(INS_sve_decp, EA_8BYTE, REG_R1, REG_P1, INS_OPTS_SCALABLE_H); // DECP , . @@ -5494,6 +5512,21 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R(INS_sve_whilelt, EA_8BYTE, REG_P15, REG_R14, REG_R15, INS_OPTS_SCALABLE_B, INS_SCALABLE_OPTS_VL_4X); // WHILELT ., , , + // IF_SVE_DZ_1A + theEmitter->emitIns_R(INS_sve_ptrue, EA_SCALABLE, REG_P8, INS_OPTS_SCALABLE_B); // PTRUE . + theEmitter->emitIns_R(INS_sve_ptrue, EA_SCALABLE, REG_P9, INS_OPTS_SCALABLE_H); // PTRUE . + theEmitter->emitIns_R(INS_sve_ptrue, EA_SCALABLE, REG_P10, INS_OPTS_SCALABLE_S); // PTRUE . + theEmitter->emitIns_R(INS_sve_ptrue, EA_SCALABLE, REG_P11, INS_OPTS_SCALABLE_D); // PTRUE . + + // IF_SVE_EA_1A + // Note: B is reserved + theEmitter->emitIns_R_F(INS_sve_fdup, EA_SCALABLE, REG_V0, 2.0, INS_OPTS_SCALABLE_H); // FDUP ., # + theEmitter->emitIns_R_F(INS_sve_fdup, EA_SCALABLE, REG_V1, 1.0, INS_OPTS_SCALABLE_S); // FDUP ., # + theEmitter->emitIns_R_F(INS_sve_fdup, EA_SCALABLE, REG_V2, 2.0, INS_OPTS_SCALABLE_D); // FDUP ., # + theEmitter->emitIns_R_F(INS_sve_fmov, EA_SCALABLE, REG_V3, -10.0, INS_OPTS_SCALABLE_H); // FMOV ., # + theEmitter->emitIns_R_F(INS_sve_fmov, EA_SCALABLE, REG_V4, -0.125, INS_OPTS_SCALABLE_S); // FMOV ., # + theEmitter->emitIns_R_F(INS_sve_fmov, EA_SCALABLE, REG_V5, 31.0, INS_OPTS_SCALABLE_D); // FMOV ., # + // IF_SVE_IH_3A theEmitter->emitIns_R_R_R_I(INS_sve_ld1d, EA_SCALABLE, REG_V5, REG_P3, REG_R4, 0, INS_OPTS_SCALABLE_D); // LD1D {.D }, /Z, [{, #, MUL VL}] diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 9959ebf3a9bb22..be18133b3d78c7 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1242,14 +1242,16 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isScalableVectorSize(id->idOpSize())); break; - case IF_SVE_DO_2A: // ........xx...... .....X.MMMMddddd -- SVE saturating inc/dec register by predicate count - assert(isValidGeneralDatasize(id->idOpSize())); // X + case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) + assert(id->idOpSize() == EA_8BYTE); FALLTHROUGH; + case IF_SVE_DO_2A: // ........xx...... .....X.MMMMddddd -- SVE saturating inc/dec register by predicate count case IF_SVE_DM_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec register by predicate count - assert(insOptsScalableStandard(id->idInsOpt())); // xx - assert(isGeneralRegister(id->idReg1())); // ddddd - assert(isPredicateRegister(id->idReg2())); // MMMM + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + assert(isGeneralRegister(id->idReg1())); // ddddd + assert(isPredicateRegister(id->idReg2())); // MMMM assert(isValidGeneralDatasize(id->idOpSize())); break; @@ -1339,6 +1341,19 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx break; + case IF_SVE_DZ_1A: // ........xx...... .............DDD -- sve_int_pn_ptrue + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isHighPredicateRegister(id->idReg1())); // DDD + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + + case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) + assert(insOptsScalableAtLeastHalf(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + assert(isValidUimm8(emitGetInsSC(id))); // iiiiiiii + break; + case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus // immediate) case IF_SVE_IH_3A_A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus @@ -5857,7 +5872,7 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, ssize_t imm) * Add an instruction referencing a single register. */ -void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) +void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts opt /* = INS_OPTS_NONE */) { insFormat fmt = IF_NONE; instrDesc* id = emitNewInstrSmall(attr); @@ -5900,6 +5915,15 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) fmt = IF_SVE_DR_1A; break; + case INS_sve_ptrue: + assert(insOptsScalableStandard(opt)); + assert(isHighPredicateRegister(reg)); // DDD + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + id->idReg1(reg); + id->idInsOpt(opt); + fmt = IF_SVE_DZ_1A; + break; + default: unreached(); } @@ -6251,6 +6275,21 @@ void emitter::emitIns_R_F( } break; + case INS_sve_fmov: + case INS_sve_fdup: + assert(insOptsScalableAtLeastHalf(opt)); + assert(isVectorRegister(reg)); // ddddd + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + + fpi.immFPIVal = 0; + canEncode = canEncodeFloatImm8(immDbl, &fpi); + imm = fpi.immFPIVal; + fmt = IF_SVE_EA_1A; + + // FMOV is an alias for FDUP, and is always the preferred disassembly. + ins = INS_sve_fmov; + break; + default: unreached(); break; @@ -6444,8 +6483,12 @@ void emitter::emitIns_Mov( * Add an instruction referencing two registers */ -void emitter::emitIns_R_R( - instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts opt /* = INS_OPTS_NONE */) +void emitter::emitIns_R_R(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + insOpts opt /* = INS_OPTS_NONE */, + insScalableOpts sopt /* = INS_SCALABLE_OPTS_NONE */) { if (IsMovInstruction(ins)) { @@ -7090,6 +7133,15 @@ void emitter::emitIns_R_R( } break; + case INS_sve_cntp: + assert(insOptsScalableStandard(opt)); + assert(insScalableOptsWithVectorLength(sopt)); // l + assert(isGeneralRegister(reg1)); // ddddd + assert(isPredicateRegister(reg2)); // NNNN + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + fmt = IF_SVE_DL_2A; + break; + case INS_sve_incp: case INS_sve_decp: assert(isPredicateRegister(reg2)); // MMMM @@ -7181,7 +7233,17 @@ void emitter::emitIns_R_R( assert(fmt != IF_NONE); - instrDesc* id = emitNewInstrSmall(attr); + instrDesc* id; + + if (insScalableOptsWithVectorLength(sopt)) + { + id = emitNewInstr(attr); + id->idVectorLength4x(sopt == INS_SCALABLE_OPTS_VL_4X); + } + else + { + id = emitNewInstrSmall(attr); + } id->idIns(ins); id->idInsFmt(fmt); @@ -13090,7 +13152,16 @@ void emitter::emitIns_Call(EmitCallType callType, if (id->idVectorLength4x()) { - return 0x2000; // set the bit at location 13 + switch (id->idInsFmt()) + { + case IF_SVE_DL_2A: + return 0x400; // set the bit at location 10 + case IF_SVE_DY_3A: + return 0x2000; // set the bit at location 13 + default: + assert(!"Unexpected format"); + break; + } } return 0; @@ -16380,6 +16451,15 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeVectorLengthSpecifier(id); // l + code |= insEncodeReg_R_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_P_8_to_5(id->idReg2()); // NNNN + code |= insEncodeSveElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_DM_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec register by predicate count code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_R_4_to_0(id->idReg1()); // ddddd @@ -16478,6 +16558,21 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_DZ_1A: // ........xx...... .............DDD -- sve_int_pn_ptrue + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_2_to_0(id->idReg1()); // DDD + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= ((code_t)emitGetInsSC(id) << 5); // iiiiiiii + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_DU_3A: // ........xx.mmmmm ......nnnnn.DDDD -- SVE pointer conflict compare code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD @@ -19032,6 +19127,13 @@ void emitter::emitDispInsHelp( emitDispImm(emitGetInsSC(id), false); // iiii break; + // , ., + case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) + emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd + emitDispPredicateReg(id->idReg2(), PREDICATE_SIZED, id->idInsOpt(), true); // NNNN + emitDispVectorLengthSpecifier(id); + break; + // , . case IF_SVE_DM_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec register by predicate count emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd @@ -19130,6 +19232,18 @@ void emitter::emitDispInsHelp( emitDispVectorLengthSpecifier(id); break; + // PTRUE . + case IF_SVE_DZ_1A: // ........xx...... .............DDD -- sve_int_pn_ptrue + emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), false); // DDD + break; + + // FDUP ., # + // FMOV ., # + case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispFloatImm(emitGetInsSC(id)); // iiiiiiii + break; + // { .D }, /Z, [{, #, MUL VL}] // Some of these formats may allow changing the element size instead of using 'D' for all instructions. case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus @@ -21747,6 +21861,11 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins } break; + case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + case IF_SVE_DM_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec register by predicate count case IF_SVE_DN_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec vector by predicate count case IF_SVE_DP_2A: // ........xx...... .......MMMMddddd -- SVE saturating inc/dec vector by predicate count @@ -21802,6 +21921,16 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_3C; break; + case IF_SVE_DZ_1A: // ........xx...... .............DDD -- sve_int_pn_ptrue + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + + case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus // immediate) case IF_SVE_IH_3A_A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 5384520239435d..ef77505f58d439 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -580,7 +580,7 @@ static bool isValidUimm5(ssize_t value) return (0 <= value) && (value <= 0x1FLL); }; -// Returns true if 'value' is a legal unsigned immediate 8 bit encoding (such as for fMOV). +// Returns true if 'value' is a legal unsigned immediate 8 bit encoding (such as for FMOV). static bool isValidUimm8(ssize_t value) { return (0 <= value) && (value <= 0xFFLL); @@ -1066,7 +1066,7 @@ void emitIns(instruction ins); void emitIns_I(instruction ins, emitAttr attr, ssize_t imm); -void emitIns_R(instruction ins, emitAttr attr, regNumber reg); +void emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts opt = INS_OPTS_NONE); void emitIns_R_I(instruction ins, emitAttr attr, @@ -1080,7 +1080,12 @@ void emitIns_R_F(instruction ins, emitAttr attr, regNumber reg, double immDbl, i void emitIns_Mov( instruction ins, emitAttr attr, regNumber dstReg, regNumber srcReg, bool canSkip, insOpts opt = INS_OPTS_NONE); -void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts opt = INS_OPTS_NONE); +void emitIns_R_R(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + insOpts opt = INS_OPTS_NONE, + insScalableOpts sopt = INS_SCALABLE_OPTS_NONE); void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insFlags flags) { From 06005da6d1d2bb405f2138cf61b864bed27b525a Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Wed, 17 Jan 2024 08:53:30 -0800 Subject: [PATCH 076/189] Adding vectorized implementations of Log to Vector64/128/256/512 (#96913) * Adding vectorized implementations of Log to Vector64/128/256/512 * Accelerate TensorPrimitives.Log for double * Ensure the ref assembly is updated to include the new Log method * Fix the variance for one of the Log2 tests to account for the scalar fallback --- .../netcore/TensorPrimitives.netcore.cs | 419 +++++++++++++++++- .../System/Runtime/Intrinsics/Vector128.cs | 32 ++ .../System/Runtime/Intrinsics/Vector256.cs | 32 ++ .../System/Runtime/Intrinsics/Vector512.cs | 32 ++ .../src/System/Runtime/Intrinsics/Vector64.cs | 44 ++ .../System/Runtime/Intrinsics/VectorMath.cs | 284 +++++++++++- .../ref/System.Runtime.Intrinsics.cs | 8 + .../tests/Vectors/Vector128Tests.cs | 16 + .../tests/Vectors/Vector256Tests.cs | 16 + .../tests/Vectors/Vector512Tests.cs | 16 + .../tests/Vectors/Vector64Tests.cs | 18 +- .../tests/Vectors/VectorTestMemberData.cs | 94 ++++ 12 files changed, 990 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs index 5d06bd523c6bfd..9c41234cef3f92 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs @@ -11470,6 +11470,397 @@ public static Vector512 Invoke(Vector512 t) /// T.Log(x) internal readonly struct LogOperator : IUnaryOperator where T : ILogarithmicFunctions + { + public static bool Vectorizable => (typeof(T) == typeof(double)) + || (typeof(T) == typeof(float)); + + public static T Invoke(T x) => T.Log(x); + + public static Vector128 Invoke(Vector128 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Log(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Log(x.AsSingle()).As(); + } +#else + if (typeof(T) == typeof(double)) + { + return LogOperatorDouble.Invoke(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return LogOperatorSingle.Invoke(x.AsSingle()).As(); + } +#endif + } + + public static Vector256 Invoke(Vector256 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Log(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Log(x.AsSingle()).As(); + } +#else + if (typeof(T) == typeof(double)) + { + return LogOperatorDouble.Invoke(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return LogOperatorSingle.Invoke(x.AsSingle()).As(); + } +#endif + } + + public static Vector512 Invoke(Vector512 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Log(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Log(x.AsSingle()).As(); + } +#else + if (typeof(T) == typeof(double)) + { + return LogOperatorDouble.Invoke(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return LogOperatorSingle.Invoke(x.AsSingle()).As(); + } +#endif + } + } + +#if !NET9_0_OR_GREATER + /// double.Log(x) + internal readonly struct LogOperatorDouble : IUnaryOperator + { + // This code is based on `vrd2_log` from amd/aocl-libm-ose + // Copyright (C) 2018-2020 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Reduce x into the form: + // x = (-1)^s*2^n*m + // s will be always zero, as log is defined for positive numbers + // n is an integer known as the exponent + // m is mantissa + // + // x is reduced such that the mantissa, m lies in [2/3,4/3] + // x = 2^n*m where m is in [2/3,4/3] + // log(x) = log(2^n*m) We have log(a*b) = log(a)+log(b) + // = log(2^n) + log(m) We have log(a^n) = n*log(a) + // = n*log(2) + log(m) + // = n*log(2) + log(1+(m-1)) + // = n*log(2) + log(1+f) Where f = m-1 + // = n*log(2) + log1p(f) f lies in [-1/3,+1/3] + // + // Thus we have : + // log(x) = n*log(2) + log1p(f) + // In the above, the first term n*log(2), n can be calculated by using right shift operator and the value of log(2) + // is known and is stored as a constant + // The second term log1p(F) is approximated by using a polynomial + + private const ulong V_MIN = 0x00100000_00000000; // SmallestNormal + private const ulong V_MAX = 0x7FF00000_00000000; // +Infinity + private const ulong V_MSK = 0x000FFFFF_FFFFFFFF; // (1 << 52) - 1 + private const ulong V_OFF = 0x3FE55555_55555555; // 2.0 / 3.0 + + private const double LN2_HEAD = 0.693359375; + private const double LN2_TAIL = -0.00021219444005469057; + + private const double C02 = -0.499999999999999560; + private const double C03 = +0.333333333333414750; + private const double C04 = -0.250000000000297430; + private const double C05 = +0.199999999975985220; + private const double C06 = -0.166666666608919500; + private const double C07 = +0.142857145600277100; + private const double C08 = -0.125000005127831270; + private const double C09 = +0.111110952357159440; + private const double C10 = -0.099999750495501240; + private const double C11 = +0.090914349823462390; + private const double C12 = -0.083340600527551860; + private const double C13 = +0.076817603328311300; + private const double C14 = -0.071296718946287310; + private const double C15 = +0.067963465211535730; + private const double C16 = -0.063995035098960040; + private const double C17 = +0.049370587082412105; + private const double C18 = -0.045370170994891980; + private const double C19 = +0.088970636003577750; + private const double C20 = -0.086906174116908760; + + public static bool Vectorizable => true; + + public static double Invoke(double x) => double.Log(x); + + public static Vector128 Invoke(Vector128 x) + { + Vector128 specialResult = x; + + // x is zero, subnormal, infinity, or NaN + Vector128 specialMask = Vector128.GreaterThanOrEqual(x.AsUInt64() - Vector128.Create(V_MIN), Vector128.Create(V_MAX - V_MIN)); + + if (specialMask != Vector128.Zero) + { + Vector128 xBits = x.AsInt64(); + + // (x < 0) ? float.NaN : x + Vector128 lessThanZeroMask = Vector128.LessThan(xBits, Vector128.Zero).AsDouble(); + + specialResult = Vector128.ConditionalSelect( + lessThanZeroMask, + Vector128.Create(double.NaN), + specialResult + ); + + // double.IsZero(x) ? double.NegativeInfinity : x + Vector128 zeroMask = Vector128.Equals(xBits << 1, Vector128.Zero).AsDouble(); + + specialResult = Vector128.ConditionalSelect( + zeroMask, + Vector128.Create(double.NegativeInfinity), + specialResult + ); + + // double.IsZero(x) | (x < 0) | double.IsNaN(x) | double.IsPositiveInfinity(x) + Vector128 temp = zeroMask + | lessThanZeroMask + | Vector128.GreaterThanOrEqual(xBits, Vector128.Create(double.PositiveInfinity).AsInt64()).AsDouble(); + + // subnormal + Vector128 subnormalMask = Vector128.AndNot(specialMask.AsDouble(), temp); + + // multiply by 2^52, then normalize + x = Vector128.ConditionalSelect( + subnormalMask, + ((x * 4503599627370496.0).AsUInt64() - Vector128.Create(52ul << 52)).AsDouble(), + x + ); + + specialMask = temp.AsUInt64(); + } + + // Reduce the mantissa to [+2/3, +4/3] + Vector128 vx = x.AsUInt64() - Vector128.Create(V_OFF); + Vector128 n = Vector128.ConvertToDouble(vx.AsInt64() >> 52); + vx = (vx & Vector128.Create(V_MSK)) + Vector128.Create(V_OFF); + + // Adjust the mantissa to [-1/3, +1/3] + Vector128 r = vx.AsDouble() - Vector128.One; + + Vector128 r02 = r * r; + Vector128 r04 = r02 * r02; + Vector128 r08 = r04 * r04; + Vector128 r16 = r08 * r08; + + // Compute log(x + 1) using Polynomial approximation + // C0 + (r * C1) + (r^2 * C2) + ... + (r^20 * C20) + + Vector128 poly = (((r04 * C20) + + ((((r * C19) + Vector128.Create(C18)) * r02) + + ((r * C17) + Vector128.Create(C16)))) * r16) + + (((((((r * C15) + Vector128.Create(C14)) * r02) + + ((r * C13) + Vector128.Create(C12))) * r04) + + ((((r * C11) + Vector128.Create(C10)) * r02) + + ((r * C09) + Vector128.Create(C08)))) * r08) + + (((((r * C07) + Vector128.Create(C06)) * r02) + + ((r * C05) + Vector128.Create(C04))) * r04) + + ((((r * C03) + Vector128.Create(C02)) * r02) + r); + + return Vector128.ConditionalSelect( + specialMask.AsDouble(), + specialResult, + (n * LN2_HEAD) + ((n * LN2_TAIL) + poly) + ); + } + + public static Vector256 Invoke(Vector256 x) + { + Vector256 specialResult = x; + + // x is zero, subnormal, infinity, or NaN + Vector256 specialMask = Vector256.GreaterThanOrEqual(x.AsUInt64() - Vector256.Create(V_MIN), Vector256.Create(V_MAX - V_MIN)); + + if (specialMask != Vector256.Zero) + { + Vector256 xBits = x.AsInt64(); + + // (x < 0) ? float.NaN : x + Vector256 lessThanZeroMask = Vector256.LessThan(xBits, Vector256.Zero).AsDouble(); + + specialResult = Vector256.ConditionalSelect( + lessThanZeroMask, + Vector256.Create(double.NaN), + specialResult + ); + + // double.IsZero(x) ? double.NegativeInfinity : x + Vector256 zeroMask = Vector256.Equals(xBits << 1, Vector256.Zero).AsDouble(); + + specialResult = Vector256.ConditionalSelect( + zeroMask, + Vector256.Create(double.NegativeInfinity), + specialResult + ); + + // double.IsZero(x) | (x < 0) | double.IsNaN(x) | double.IsPositiveInfinity(x) + Vector256 temp = zeroMask + | lessThanZeroMask + | Vector256.GreaterThanOrEqual(xBits, Vector256.Create(double.PositiveInfinity).AsInt64()).AsDouble(); + + // subnormal + Vector256 subnormalMask = Vector256.AndNot(specialMask.AsDouble(), temp); + + // multiply by 2^52, then normalize + x = Vector256.ConditionalSelect( + subnormalMask, + ((x * 4503599627370496.0).AsUInt64() - Vector256.Create(52ul << 52)).AsDouble(), + x + ); + + specialMask = temp.AsUInt64(); + } + + // Reduce the mantissa to [+2/3, +4/3] + Vector256 vx = x.AsUInt64() - Vector256.Create(V_OFF); + Vector256 n = Vector256.ConvertToDouble(vx.AsInt64() >> 52); + vx = (vx & Vector256.Create(V_MSK)) + Vector256.Create(V_OFF); + + // Adjust the mantissa to [-1/3, +1/3] + Vector256 r = vx.AsDouble() - Vector256.One; + + Vector256 r02 = r * r; + Vector256 r04 = r02 * r02; + Vector256 r08 = r04 * r04; + Vector256 r16 = r08 * r08; + + // Compute log(x + 1) using Polynomial approximation + // C0 + (r * C1) + (r^2 * C2) + ... + (r^20 * C20) + + Vector256 poly = (((r04 * C20) + + ((((r * C19) + Vector256.Create(C18)) * r02) + + ((r * C17) + Vector256.Create(C16)))) * r16) + + (((((((r * C15) + Vector256.Create(C14)) * r02) + + ((r * C13) + Vector256.Create(C12))) * r04) + + ((((r * C11) + Vector256.Create(C10)) * r02) + + ((r * C09) + Vector256.Create(C08)))) * r08) + + (((((r * C07) + Vector256.Create(C06)) * r02) + + ((r * C05) + Vector256.Create(C04))) * r04) + + ((((r * C03) + Vector256.Create(C02)) * r02) + r); + + return Vector256.ConditionalSelect( + specialMask.AsDouble(), + specialResult, + (n * LN2_HEAD) + ((n * LN2_TAIL) + poly) + ); + } + + public static Vector512 Invoke(Vector512 x) + { + Vector512 specialResult = x; + + // x is zero, subnormal, infinity, or NaN + Vector512 specialMask = Vector512.GreaterThanOrEqual(x.AsUInt64() - Vector512.Create(V_MIN), Vector512.Create(V_MAX - V_MIN)); + + if (specialMask != Vector512.Zero) + { + Vector512 xBits = x.AsInt64(); + + // (x < 0) ? float.NaN : x + Vector512 lessThanZeroMask = Vector512.LessThan(xBits, Vector512.Zero).AsDouble(); + + specialResult = Vector512.ConditionalSelect( + lessThanZeroMask, + Vector512.Create(double.NaN), + specialResult + ); + + // double.IsZero(x) ? double.NegativeInfinity : x + Vector512 zeroMask = Vector512.Equals(xBits << 1, Vector512.Zero).AsDouble(); + + specialResult = Vector512.ConditionalSelect( + zeroMask, + Vector512.Create(double.NegativeInfinity), + specialResult + ); + + // double.IsZero(x) | (x < 0) | double.IsNaN(x) | double.IsPositiveInfinity(x) + Vector512 temp = zeroMask + | lessThanZeroMask + | Vector512.GreaterThanOrEqual(xBits, Vector512.Create(double.PositiveInfinity).AsInt64()).AsDouble(); + + // subnormal + Vector512 subnormalMask = Vector512.AndNot(specialMask.AsDouble(), temp); + + // multiply by 2^52, then normalize + x = Vector512.ConditionalSelect( + subnormalMask, + ((x * 4503599627370496.0).AsUInt64() - Vector512.Create(52ul << 52)).AsDouble(), + x + ); + + specialMask = temp.AsUInt64(); + } + + // Reduce the mantissa to [+2/3, +4/3] + Vector512 vx = x.AsUInt64() - Vector512.Create(V_OFF); + Vector512 n = Vector512.ConvertToDouble(vx.AsInt64() >> 52); + vx = (vx & Vector512.Create(V_MSK)) + Vector512.Create(V_OFF); + + // Adjust the mantissa to [-1/3, +1/3] + Vector512 r = vx.AsDouble() - Vector512.One; + + Vector512 r02 = r * r; + Vector512 r04 = r02 * r02; + Vector512 r08 = r04 * r04; + Vector512 r16 = r08 * r08; + + // Compute log(x + 1) using Polynomial approximation + // C0 + (r * C1) + (r^2 * C2) + ... + (r^20 * C20) + + Vector512 poly = (((r04 * C20) + + ((((r * C19) + Vector512.Create(C18)) * r02) + + ((r * C17) + Vector512.Create(C16)))) * r16) + + (((((((r * C15) + Vector512.Create(C14)) * r02) + + ((r * C13) + Vector512.Create(C12))) * r04) + + ((((r * C11) + Vector512.Create(C10)) * r02) + + ((r * C09) + Vector512.Create(C08)))) * r08) + + (((((r * C07) + Vector512.Create(C06)) * r02) + + ((r * C05) + Vector512.Create(C04))) * r04) + + ((((r * C03) + Vector512.Create(C02)) * r02) + r); + + return Vector512.ConditionalSelect( + specialMask.AsDouble(), + specialResult, + (n * LN2_HEAD) + ((n * LN2_TAIL) + poly) + ); + } + } + + /// float.Log(x) + internal readonly struct LogOperatorSingle : IUnaryOperator { // This code is based on `vrs4_logf` from amd/aocl-libm-ose // Copyright (C) 2018-2019 Advanced Micro Devices, Inc. All rights reserved. @@ -11542,15 +11933,12 @@ public static Vector512 Invoke(Vector512 t) private const float C9 = 0.14401625f; private const float C10 = -0.13657966f; - public static bool Vectorizable => typeof(T) == typeof(float); + public static bool Vectorizable => true; - public static T Invoke(T x) => T.Log(x); + public static float Invoke(float x) => float.Log(x); - public static Vector128 Invoke(Vector128 t) + public static Vector128 Invoke(Vector128 x) { - Debug.Assert(typeof(T) == typeof(float)); - Vector128 x = t.AsSingle(); - Vector128 specialResult = x; // x is subnormal or infinity or NaN @@ -11615,14 +12003,11 @@ public static Vector128 Invoke(Vector128 t) specialMask.AsSingle(), specialResult, n * Vector128.Create(V_LN2) + q - ).As(); + ); } - public static Vector256 Invoke(Vector256 t) + public static Vector256 Invoke(Vector256 x) { - Debug.Assert(typeof(T) == typeof(float)); - Vector256 x = t.AsSingle(); - Vector256 specialResult = x; // x is subnormal or infinity or NaN @@ -11687,14 +12072,11 @@ public static Vector256 Invoke(Vector256 t) specialMask.AsSingle(), specialResult, n * Vector256.Create(V_LN2) + q - ).As(); + ); } - public static Vector512 Invoke(Vector512 t) + public static Vector512 Invoke(Vector512 x) { - Debug.Assert(typeof(T) == typeof(float)); - Vector512 x = t.AsSingle(); - Vector512 specialResult = x; // x is subnormal or infinity or NaN @@ -11759,9 +12141,10 @@ public static Vector512 Invoke(Vector512 t) specialMask.AsSingle(), specialResult, n * Vector512.Create(V_LN2) + q - ).As(); + ); } } +#endif /// T.Log2(x) internal readonly struct Log2Operator : IUnaryOperator @@ -11880,7 +12263,7 @@ public static Vector512 Invoke(Vector512 x) private const ulong V_MIN = 0x00100000_00000000; // SmallestNormal private const ulong V_MAX = 0x7FF00000_00000000; // +Infinity private const ulong V_MSK = 0x000FFFFF_FFFFFFFF; // (1 << 52) - 1 - private const ulong V_OFF = 0x3FE55555_55555555; // 2.0 / 3.0\ + private const ulong V_OFF = 0x3FE55555_55555555; // 2.0 / 3.0 private const double LN2_HEAD = 1.44269180297851562500E+00; private const double LN2_TAIL = 3.23791044778235969970E-06; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index ea8cf23dd06ecb..0342f1b144aac6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -1781,6 +1781,38 @@ internal static Vector128 LoadUnsafe(ref char source) => internal static Vector128 LoadUnsafe(ref char source, nuint elementOffset) => LoadUnsafe(ref Unsafe.As(ref source), elementOffset); + /// + public static Vector128 Log(Vector128 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogDouble, Vector128, Vector128>(vector); + } + else + { + return Create( + Vector64.Log(vector._lower), + Vector64.Log(vector._upper) + ); + } + } + + /// + public static Vector128 Log(Vector128 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogSingle, Vector128, Vector128>(vector); + } + else + { + return Create( + Vector64.Log(vector._lower), + Vector64.Log(vector._upper) + ); + } + } + /// public static Vector128 Log2(Vector128 vector) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index 7d70941d31b8a2..9a5422e2385187 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -1755,6 +1755,38 @@ internal static Vector256 LoadUnsafe(ref char source) => internal static Vector256 LoadUnsafe(ref char source, nuint elementOffset) => LoadUnsafe(ref Unsafe.As(ref source), elementOffset); + /// + public static Vector256 Log(Vector256 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogDouble, Vector256, Vector256>(vector); + } + else + { + return Create( + Vector128.Log(vector._lower), + Vector128.Log(vector._upper) + ); + } + } + + /// + public static Vector256 Log(Vector256 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogSingle, Vector256, Vector256>(vector); + } + else + { + return Create( + Vector128.Log(vector._lower), + Vector128.Log(vector._upper) + ); + } + } + /// public static Vector256 Log2(Vector256 vector) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index b926ff67edf662..42685879ebb05e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -1806,6 +1806,38 @@ internal static Vector512 LoadUnsafe(ref char source) => internal static Vector512 LoadUnsafe(ref char source, nuint elementOffset) => LoadUnsafe(ref Unsafe.As(ref source), elementOffset); + /// + public static Vector512 Log(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogDouble, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Log(vector._lower), + Vector256.Log(vector._upper) + ); + } + } + + /// + public static Vector512 Log(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogSingle, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Log(vector._lower), + Vector256.Log(vector._upper) + ); + } + } + /// public static Vector512 Log2(Vector512 vector) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index 79de8e2ad2ef3b..e3e4f8b3b40b02 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -1571,6 +1571,50 @@ public static Vector64 LoadUnsafe(ref readonly T source, nuint elementOffs return Unsafe.ReadUnaligned>(in address); } + internal static Vector64 Log(Vector64 vector) + where T : ILogarithmicFunctions + { + Unsafe.SkipInit(out Vector64 result); + + for (int index = 0; index < Vector64.Count; index++) + { + T value = T.Log(vector.GetElement(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + + /// Computes the log of each element in a vector. + /// The vector that will have its log computed. + /// A vector whose elements are the log of the elements in . + public static Vector64 Log(Vector64 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogDouble, Vector64, Vector64>(vector); + } + else + { + return Log(vector); + } + } + + /// Computes the log of each element in a vector. + /// The vector that will have its log computed. + /// A vector whose elements are the log of the elements in . + public static Vector64 Log(Vector64 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.LogSingle, Vector64, Vector64>(vector); + } + else + { + return Log(vector); + } + } + internal static Vector64 Log2(Vector64 vector) where T : ILogarithmicFunctions { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs index 8cdb1d91010bca..20e5cf83bc47a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs @@ -7,6 +7,288 @@ namespace System.Runtime.Intrinsics { internal static class VectorMath { + public static TVectorDouble LogDouble(TVectorDouble x) + where TVectorDouble : unmanaged, ISimdVector + where TVectorInt64 : unmanaged, ISimdVector + where TVectorUInt64 : unmanaged, ISimdVector + { + // This code is based on `vrd2_log` from amd/aocl-libm-ose + // Copyright (C) 2018-2020 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Reduce x into the form: + // x = (-1)^s*2^n*m + // s will be always zero, as log is defined for positive numbers + // n is an integer known as the exponent + // m is mantissa + // + // x is reduced such that the mantissa, m lies in [2/3,4/3] + // x = 2^n*m where m is in [2/3,4/3] + // log(x) = log(2^n*m) We have log(a*b) = log(a)+log(b) + // = log(2^n) + log(m) We have log(a^n) = n*log(a) + // = n*log(2) + log(m) + // = n*log(2) + log(1+(m-1)) + // = n*log(2) + log(1+f) Where f = m-1 + // = n*log(2) + log1p(f) f lies in [-1/3,+1/3] + // + // Thus we have : + // log(x) = n*log(2) + log1p(f) + // In the above, the first term n*log(2), n can be calculated by using right shift operator and the value of log(2) + // is known and is stored as a constant + // The second term log1p(F) is approximated by using a polynomial + + const ulong V_MIN = double.SmallestNormalBits; + const ulong V_MAX = double.PositiveInfinityBits; + const ulong V_MSK = 0x000FFFFF_FFFFFFFF; // (1 << 52) - 1 + const ulong V_OFF = 0x3FE55555_55555555; // 2.0 / 3.0 + + const double LN2_HEAD = 0.693359375; + const double LN2_TAIL = -0.00021219444005469057; + + const double C02 = -0.499999999999999560; + const double C03 = +0.333333333333414750; + const double C04 = -0.250000000000297430; + const double C05 = +0.199999999975985220; + const double C06 = -0.166666666608919500; + const double C07 = +0.142857145600277100; + const double C08 = -0.125000005127831270; + const double C09 = +0.111110952357159440; + const double C10 = -0.099999750495501240; + const double C11 = +0.090914349823462390; + const double C12 = -0.083340600527551860; + const double C13 = +0.076817603328311300; + const double C14 = -0.071296718946287310; + const double C15 = +0.067963465211535730; + const double C16 = -0.063995035098960040; + const double C17 = +0.049370587082412105; + const double C18 = -0.045370170994891980; + const double C19 = +0.088970636003577750; + const double C20 = -0.086906174116908760; + + TVectorDouble specialResult = x; + + // x is zero, subnormal, infinity, or NaN + TVectorUInt64 specialMask = TVectorUInt64.GreaterThanOrEqual(Unsafe.BitCast(x) - TVectorUInt64.Create(V_MIN), TVectorUInt64.Create(V_MAX - V_MIN)); + + if (specialMask != TVectorUInt64.Zero) + { + TVectorInt64 xBits = Unsafe.BitCast(x); + + // (x < 0) ? float.NaN : x + TVectorDouble lessThanZeroMask = Unsafe.BitCast(TVectorInt64.LessThan(xBits, TVectorInt64.Zero)); + + specialResult = TVectorDouble.ConditionalSelect( + lessThanZeroMask, + TVectorDouble.Create(double.NaN), + specialResult + ); + + // double.IsZero(x) ? double.NegativeInfinity : x + TVectorDouble zeroMask = Unsafe.BitCast(TVectorInt64.Equals(xBits << 1, TVectorInt64.Zero)); + + specialResult = TVectorDouble.ConditionalSelect( + zeroMask, + TVectorDouble.Create(double.NegativeInfinity), + specialResult + ); + + // double.IsZero(x) | (x < 0) | double.IsNaN(x) | double.IsPositiveInfinity(x) + TVectorDouble temp = zeroMask + | lessThanZeroMask + | Unsafe.BitCast(TVectorInt64.GreaterThanOrEqual(xBits, TVectorInt64.Create(double.PositiveInfinityBits))); + + // subnormal + TVectorDouble subnormalMask = TVectorDouble.AndNot(Unsafe.BitCast(specialMask), temp); + + // multiply by 2^52, then normalize + x = TVectorDouble.ConditionalSelect( + subnormalMask, + Unsafe.BitCast(Unsafe.BitCast(x * 4503599627370496.0) - TVectorUInt64.Create(52ul << 52)), + x + ); + + specialMask = Unsafe.BitCast(temp); + } + + // Reduce the mantissa to [+2/3, +4/3] + TVectorUInt64 vx = Unsafe.BitCast(x) - TVectorUInt64.Create(V_OFF); + TVectorDouble n = ConvertToDouble(Unsafe.BitCast(vx) >> 52); + vx = (vx & TVectorUInt64.Create(V_MSK)) + TVectorUInt64.Create(V_OFF); + + // Adjust the mantissa to [-1/3, +1/3] + TVectorDouble r = Unsafe.BitCast(vx) - TVectorDouble.One; + + TVectorDouble r02 = r * r; + TVectorDouble r04 = r02 * r02; + TVectorDouble r08 = r04 * r04; + TVectorDouble r16 = r08 * r08; + + // Compute log(x + 1) using Polynomial approximation + // C0 + (r * C1) + (r^2 * C2) + ... + (r^20 * C20) + + TVectorDouble poly = (((r04 * C20) + + ((((r * C19) + TVectorDouble.Create(C18)) * r02) + + ((r * C17) + TVectorDouble.Create(C16)))) * r16) + + (((((((r * C15) + TVectorDouble.Create(C14)) * r02) + + ((r * C13) + TVectorDouble.Create(C12))) * r04) + + ((((r * C11) + TVectorDouble.Create(C10)) * r02) + + ((r * C09) + TVectorDouble.Create(C08)))) * r08) + + (((((r * C07) + TVectorDouble.Create(C06)) * r02) + + ((r * C05) + TVectorDouble.Create(C04))) * r04) + + ((((r * C03) + TVectorDouble.Create(C02)) * r02) + r); + + return TVectorDouble.ConditionalSelect( + Unsafe.BitCast(specialMask), + specialResult, + (n * LN2_HEAD) + ((n * LN2_TAIL) + poly) + ); + } + + public static TVectorSingle LogSingle(TVectorSingle x) + where TVectorSingle : unmanaged, ISimdVector + where TVectorInt32 : unmanaged, ISimdVector + where TVectorUInt32 : unmanaged, ISimdVector + { + // This code is based on `vrs4_logf` from amd/aocl-libm-ose + // Copyright (C) 2018-2019 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Spec: + // logf(x) + // = logf(x) if x ∈ F and x > 0 + // = x if x = qNaN + // = 0 if x = 1 + // = -inf if x = (-0, 0} + // = NaN otherwise + // + // Assumptions/Expectations + // - ULP is derived to be << 4 (always) + // - Some FPU Exceptions may not be available + // - Performance is at least 3x + // + // Implementation Notes: + // 1. Range Reduction: + // x = 2^n*(1+f) .... (1) + // where n is exponent and is an integer + // (1+f) is mantissa ∈ [1,2). i.e., 1 ≤ 1+f < 2 .... (2) + // + // From (1), taking log on both sides + // log(x) = log(2^n * (1+f)) + // = log(2^n) + log(1+f) + // = n*log(2) + log(1+f) .... (3) + // + // let z = 1 + f + // log(z) = log(k) + log(z) - log(k) + // log(z) = log(kz) - log(k) + // + // From (2), range of z is [1, 2) + // by simply dividing range by 'k', z is in [1/k, 2/k) .... (4) + // Best choice of k is the one which gives equal and opposite values + // at extrema +- -+ + // 1 | 2 | + // --- - 1 = - |--- - 1 | + // k | k | .... (5) + // +- -+ + // + // Solving for k, k = 3/2, + // From (4), using 'k' value, range is therefore [-0.3333, 0.3333] + // + // 2. Polynomial Approximation: + // More information refer to tools/sollya/vrs4_logf.sollya + // + // 7th Deg - Error abs: 0x1.04c4ac98p-22 rel: 0x1.2216e6f8p-19 + // 6th Deg - Error abs: 0x1.179e97d8p-19 rel: 0x1.db676c1p-17 + + const uint V_MIN = 0x00800000; + const uint V_MAX = 0x7F800000; + const uint V_MASK = 0x007FFFFF; + const uint V_OFF = 0x3F2AAAAB; + + const float V_LN2 = 0.6931472f; + + const float C0 = 0.0f; + const float C1 = 1.0f; + const float C2 = -0.5000001f; + const float C3 = 0.33332965f; + const float C4 = -0.24999046f; + const float C5 = 0.20018855f; + const float C6 = -0.16700386f; + const float C7 = 0.13902695f; + const float C8 = -0.1197452f; + const float C9 = 0.14401625f; + const float C10 = -0.13657966f; + + TVectorSingle specialResult = x; + + // x is subnormal or infinity or NaN + TVectorUInt32 specialMask = TVectorUInt32.GreaterThanOrEqual(Unsafe.BitCast(x) - TVectorUInt32.Create(V_MIN), TVectorUInt32.Create(V_MAX - V_MIN)); + + if (specialMask != TVectorUInt32.Zero) + { + // float.IsZero(x) ? float.NegativeInfinity : x + TVectorSingle zeroMask = TVectorSingle.Equals(x, TVectorSingle.Zero); + + specialResult = TVectorSingle.ConditionalSelect( + zeroMask, + TVectorSingle.Create(float.NegativeInfinity), + specialResult + ); + + // (x < 0) ? float.NaN : x + TVectorSingle lessThanZeroMask = TVectorSingle.LessThan(x, TVectorSingle.Zero); + + specialResult = TVectorSingle.ConditionalSelect( + lessThanZeroMask, + TVectorSingle.Create(float.NaN), + specialResult + ); + + // float.IsZero(x) | (x < 0) | float.IsNaN(x) | float.IsPositiveInfinity(x) + TVectorSingle temp = zeroMask + | lessThanZeroMask + | ~TVectorSingle.Equals(x, x) + | TVectorSingle.Equals(x, TVectorSingle.Create(float.PositiveInfinity)); + + // subnormal + TVectorSingle subnormalMask = TVectorSingle.AndNot(Unsafe.BitCast(specialMask), temp); + + x = TVectorSingle.ConditionalSelect( + subnormalMask, + Unsafe.BitCast(Unsafe.BitCast(x * 8388608.0f) - TVectorUInt32.Create(23u << 23)), + x + ); + + specialMask = Unsafe.BitCast(temp); + } + + TVectorUInt32 vx = Unsafe.BitCast(x) - TVectorUInt32.Create(V_OFF); + TVectorSingle n = ConvertToSingle(Unsafe.BitCast(vx) >> 23); + + vx = (vx & TVectorUInt32.Create(V_MASK)) + TVectorUInt32.Create(V_OFF); + + TVectorSingle r = Unsafe.BitCast(vx) - TVectorSingle.Create(1.0f); + + TVectorSingle r2 = r * r; + TVectorSingle r4 = r2 * r2; + TVectorSingle r8 = r4 * r4; + + TVectorSingle q = (TVectorSingle.Create(C10) * r2 + (TVectorSingle.Create(C9) * r + TVectorSingle.Create(C8))) + * r8 + (((TVectorSingle.Create(C7) * r + TVectorSingle.Create(C6)) + * r2 + (TVectorSingle.Create(C5) * r + TVectorSingle.Create(C4))) + * r4 + ((TVectorSingle.Create(C3) * r + TVectorSingle.Create(C2)) + * r2 + (TVectorSingle.Create(C1) * r + TVectorSingle.Create(C0)))); + + return TVectorSingle.ConditionalSelect( + Unsafe.BitCast(specialMask), + specialResult, + n * TVectorSingle.Create(V_LN2) + q + ); + } + public static TVectorDouble Log2Double(TVectorDouble x) where TVectorDouble : unmanaged, ISimdVector where TVectorInt64 : unmanaged, ISimdVector @@ -40,7 +322,7 @@ public static TVectorDouble Log2Double(this System.Runtime.Intrinsics.Vector128 vector, public static System.Runtime.Intrinsics.Vector128 LoadUnsafe(ref readonly T source) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector128 LoadUnsafe(ref readonly T source, nuint elementOffset) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Log(System.Runtime.Intrinsics.Vector128 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Log(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Log2(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Log2(System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Max(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } @@ -514,6 +516,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector256 vector, public static System.Runtime.Intrinsics.Vector256 LoadUnsafe(ref readonly T source) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector256 LoadUnsafe(ref readonly T source, nuint elementOffset) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Log(System.Runtime.Intrinsics.Vector256 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Log(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Log2(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Log2(System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Max(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } @@ -843,6 +847,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector512 vector, public static System.Runtime.Intrinsics.Vector512 LoadUnsafe(ref readonly T source) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector512 LoadUnsafe(ref readonly T source, nuint elementOffset) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Log(System.Runtime.Intrinsics.Vector512 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Log(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Log2(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Log2(System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Max(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } @@ -1144,6 +1150,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector64 vector, public static System.Runtime.Intrinsics.Vector64 LoadUnsafe(ref readonly T source) { throw null; } [System.CLSCompliantAttribute(false)] public static System.Runtime.Intrinsics.Vector64 LoadUnsafe(ref readonly T source, nuint elementOffset) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Log(System.Runtime.Intrinsics.Vector64 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Log(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Log2(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Log2(System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Max(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index d253dbf5085433..13ffa0292bff3f 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -4687,6 +4687,22 @@ private static void TestGetOne() Assert.Equal((Vector128)methodInfo.Invoke(null, null), Vector128.Create(T.One)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] + public void LogDoubleTest(double value, double expectedResult, double variance) + { + Vector128 actualResult = Vector128.Log(Vector128.Create(value)); + AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.LogSingle), MemberType = typeof(VectorTestMemberData))] + public void LogSingleTest(float value, float expectedResult, float variance) + { + Vector128 actualResult = Vector128.Log(Vector128.Create(value)); + AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.Log2Double), MemberType = typeof(VectorTestMemberData))] public void Log2DoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index 0f61b9d50b641b..a74bf79d65dea4 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -5702,6 +5702,22 @@ private static void TestGetOne() Assert.Equal((Vector256)methodInfo.Invoke(null, null), Vector256.Create(T.One)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] + public void LogDoubleTest(double value, double expectedResult, double variance) + { + Vector256 actualResult = Vector256.Log(Vector256.Create(value)); + AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.LogSingle), MemberType = typeof(VectorTestMemberData))] + public void LogSingleTest(float value, float expectedResult, float variance) + { + Vector256 actualResult = Vector256.Log(Vector256.Create(value)); + AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.Log2Double), MemberType = typeof(VectorTestMemberData))] public void Log2DoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index 778bf2bbc3702a..01c42d277b8553 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -5134,6 +5134,22 @@ private static void TestIsNotSupported() Assert.False((bool)methodInfo.Invoke(null, null)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] + public void LogDoubleTest(double value, double expectedResult, double variance) + { + Vector512 actualResult = Vector512.Log(Vector512.Create(value)); + AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.LogSingle), MemberType = typeof(VectorTestMemberData))] + public void LogSingleTest(float value, float expectedResult, float variance) + { + Vector512 actualResult = Vector512.Log(Vector512.Create(value)); + AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.Log2Double), MemberType = typeof(VectorTestMemberData))] public void Log2DoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs index 7706630bec31d1..ec5a0e11118cec 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs @@ -4104,6 +4104,22 @@ private static void TestGetOne() Assert.Equal((Vector64)methodInfo.Invoke(null, null), Vector64.Create(T.One)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] + public void LogDoubleTest(double value, double expectedResult, double variance) + { + Vector64 actualResult = Vector64.Log(Vector64.Create(value)); + AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.LogSingle), MemberType = typeof(VectorTestMemberData))] + public void LogSingleTest(float value, float expectedResult, float variance) + { + Vector64 actualResult = Vector64.Log(Vector64.Create(value)); + AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.Log2Double), MemberType = typeof(VectorTestMemberData))] public void Log2DoubleTest(double value, double expectedResult, double variance) @@ -4116,8 +4132,6 @@ public void Log2DoubleTest(double value, double expectedResult, double variance) [MemberData(nameof(VectorTestMemberData.Log2Single), MemberType = typeof(VectorTestMemberData))] public void Log2SingleTest(float value, float expectedResult, float variance) { - AssertExtensions.Equal(0.0f, 0.0f, 0.0f); - Vector64 actualResult = Vector64.Log2(Vector64.Create(value)); AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs index c035baa8fffd3d..cd2a20baa374d7 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs @@ -35,6 +35,100 @@ internal static class VectorTestMemberData // use CrossPlatformMachineEpsilon * 10. private const float SingleCrossPlatformMachineEpsilon = 4.76837158e-07f; + public static IEnumerable LogDouble + { + get + { + yield return new object[] { double.NegativeInfinity, double.NaN, 0.0 }; + yield return new object[] { -3.1415926535897932, double.NaN, 0.0 }; // value: -(pi) + yield return new object[] { -2.7182818284590452, double.NaN, 0.0 }; // value: -(e) + yield return new object[] { -1.4142135623730950, double.NaN, 0.0 }; // value: -(sqrt(2)) + yield return new object[] { -1.0, double.NaN, 0.0 }; + yield return new object[] { -0.69314718055994531, double.NaN, 0.0 }; // value: -(ln(2)) + yield return new object[] { -0.43429448190325183, double.NaN, 0.0 }; // value: -(log10(e)) + yield return new object[] { -0.0, double.NegativeInfinity, 0.0 }; + yield return new object[] { double.NaN, double.NaN, 0.0 }; + yield return new object[] { 0.0, double.NegativeInfinity, 0.0 }; + yield return new object[] { 0.043213918263772250, -3.1415926535897932, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(pi) + yield return new object[] { 0.065988035845312537, -2.7182818284590452, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(e) + yield return new object[] { 0.1, -2.3025850929940457, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(ln(10)) + yield return new object[] { 0.20787957635076191, -1.5707963267948966, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(pi / 2) + yield return new object[] { 0.23629008834452270, -1.4426950408889634, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(log2(e)) + yield return new object[] { 0.24311673443421421, -1.4142135623730950, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(sqrt(2)) + yield return new object[] { 0.32355726390307110, -1.1283791670955126, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: -(2 / sqrt(pi)) + yield return new object[] { 0.36787944117144232, -1.0, 0.0f }; + yield return new object[] { 0.45593812776599624, -0.78539816339744831, DoubleCrossPlatformMachineEpsilon }; // expected: -(pi / 4) + yield return new object[] { 0.49306869139523979, -0.70710678118654752, DoubleCrossPlatformMachineEpsilon }; // expected: -(1 / sqrt(2)) + yield return new object[] { 0.5, -0.69314718055994531, DoubleCrossPlatformMachineEpsilon }; // expected: -(ln(2)) + yield return new object[] { 0.52907780826773535, -0.63661977236758134, DoubleCrossPlatformMachineEpsilon }; // expected: -(2 / pi) + yield return new object[] { 0.64772148514180065, -0.43429448190325183, DoubleCrossPlatformMachineEpsilon }; // expected: -(log10(e)) + yield return new object[] { 0.72737734929521647, -0.31830988618379067, DoubleCrossPlatformMachineEpsilon }; // expected: -(1 / pi) + yield return new object[] { 1.0, 0.0, 0.0 }; + yield return new object[] { 1.3748022274393586, 0.31830988618379067, DoubleCrossPlatformMachineEpsilon }; // expected: (1 / pi) + yield return new object[] { 1.5438734439711811, 0.43429448190325183, DoubleCrossPlatformMachineEpsilon }; // expected: (log10(e)) + yield return new object[] { 1.8900811645722220, 0.63661977236758134, DoubleCrossPlatformMachineEpsilon }; // expected: (2 / pi) + yield return new object[] { 2.0, 0.69314718055994531, DoubleCrossPlatformMachineEpsilon }; // expected: (ln(2)) + yield return new object[] { 2.0281149816474725, 0.70710678118654752, DoubleCrossPlatformMachineEpsilon }; // expected: (1 / sqrt(2)) + yield return new object[] { 2.1932800507380155, 0.78539816339744831, DoubleCrossPlatformMachineEpsilon }; // expected: (pi / 4) + yield return new object[] { 2.7182818284590452, 1.0, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (e) + yield return new object[] { 3.0906430223107976, 1.1283791670955126, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (2 / sqrt(pi)) + yield return new object[] { 4.1132503787829275, 1.4142135623730950, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (sqrt(2)) + yield return new object[] { 4.2320861065570819, 1.4426950408889634, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (log2(e)) + yield return new object[] { 4.8104773809653517, 1.5707963267948966, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (pi / 2) + yield return new object[] { 10.0, 2.3025850929940457, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (ln(10)) + yield return new object[] { 15.154262241479264, 2.7182818284590452, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (e) + yield return new object[] { 23.140692632779269, 3.1415926535897932, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (pi) + yield return new object[] { double.PositiveInfinity, double.PositiveInfinity, 0.0 }; + } + } + + public static IEnumerable LogSingle + { + get + { + yield return new object[] { float.NegativeInfinity, float.NaN, 0.0f }; + yield return new object[] { -3.14159265f, float.NaN, 0.0f }; // value: -(pi) + yield return new object[] { -2.71828183f, float.NaN, 0.0f }; // value: -(e) + yield return new object[] { -1.41421356f, float.NaN, 0.0f }; // value: -(sqrt(2)) + yield return new object[] { -1.0f, float.NaN, 0.0f }; + yield return new object[] { -0.693147181f, float.NaN, 0.0f }; // value: -(ln(2)) + yield return new object[] { -0.434294482f, float.NaN, 0.0f }; // value: -(log10(e)) + yield return new object[] { -0.0f, float.NegativeInfinity, 0.0f }; + yield return new object[] { float.NaN, float.NaN, 0.0f }; + yield return new object[] { 0.0f, float.NegativeInfinity, 0.0f }; + yield return new object[] { 0.0432139183f, -3.14159265f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(pi) + yield return new object[] { 0.0659880358f, -2.71828183f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(e) + yield return new object[] { 0.1f, -2.30258509f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(ln(10)) + yield return new object[] { 0.207879576f, -1.57079633f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(pi / 2) + yield return new object[] { 0.236290088f, -1.44269504f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(log2(e)) + yield return new object[] { 0.243116734f, -1.41421356f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(sqrt(2)) + yield return new object[] { 0.323557264f, -1.12837917f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: -(2 / sqrt(pi)) + yield return new object[] { 0.367879441f, -1.0f, 0.0f }; + yield return new object[] { 0.455938128f, -0.785398163f, SingleCrossPlatformMachineEpsilon }; // expected: -(pi / 4) + yield return new object[] { 0.493068691f, -0.707106781f, SingleCrossPlatformMachineEpsilon }; // expected: -(1 / sqrt(2)) + yield return new object[] { 0.5f, -0.693147181f, SingleCrossPlatformMachineEpsilon }; // expected: -(ln(2)) + yield return new object[] { 0.529077808f, -0.636619772f, SingleCrossPlatformMachineEpsilon }; // expected: -(2 / pi) + yield return new object[] { 0.647721485f, -0.434294482f, SingleCrossPlatformMachineEpsilon }; // expected: -(log10(e)) + yield return new object[] { 0.727377349f, -0.318309886f, SingleCrossPlatformMachineEpsilon }; // expected: -(1 / pi) + yield return new object[] { 1.0f, 0.0f, 0.0f }; + yield return new object[] { 1.37480223f, 0.318309886f, SingleCrossPlatformMachineEpsilon }; // expected: (1 / pi) + yield return new object[] { 1.54387344f, 0.434294482f, SingleCrossPlatformMachineEpsilon }; // expected: (log10(e)) + yield return new object[] { 1.89008116f, 0.636619772f, SingleCrossPlatformMachineEpsilon }; // expected: (2 / pi) + yield return new object[] { 2.0f, 0.693147181f, SingleCrossPlatformMachineEpsilon }; // expected: (ln(2)) + yield return new object[] { 2.02811498f, 0.707106781f, SingleCrossPlatformMachineEpsilon }; // expected: (1 / sqrt(2)) + yield return new object[] { 2.19328005f, 0.785398163f, SingleCrossPlatformMachineEpsilon }; // expected: (pi / 4) + yield return new object[] { 2.71828183f, 1.0f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (e) + yield return new object[] { 3.09064302f, 1.12837917f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (2 / sqrt(pi)) + yield return new object[] { 4.11325038f, 1.41421356f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (sqrt(2)) + yield return new object[] { 4.23208611f, 1.44269504f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (log2(e)) + yield return new object[] { 4.81047738f, 1.57079633f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (pi / 2) + yield return new object[] { 10.0f, 2.30258509f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (ln(10)) + yield return new object[] { 15.1542622f, 2.71828183f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (e) + yield return new object[] { 23.1406926f, 3.14159265f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (pi) + yield return new object[] { float.PositiveInfinity, float.PositiveInfinity, 0.0f }; + } + } + public static IEnumerable Log2Double { get From bdbdf3ee942f5bd1efdf4c3adc03b93723ff2780 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Wed, 17 Jan 2024 09:13:09 -0800 Subject: [PATCH 077/189] Remove HybridGlobalization test name extension from iOS tests as HG is now the default and only way to run. (#97043) --- eng/testing/performance/ios_scenarios.proj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eng/testing/performance/ios_scenarios.proj b/eng/testing/performance/ios_scenarios.proj index b9efdefad6a366..1e4c59acd2ba3b 100644 --- a/eng/testing/performance/ios_scenarios.proj +++ b/eng/testing/performance/ios_scenarios.proj @@ -21,9 +21,7 @@ nosymbols - - HybridGlobalization - $(LlvmPath)$(SymbolsPath)$(HybridGlobalizationPath) + $(LlvmPath)$(SymbolsPath) From db7d26988897328a518229eee49251696b5942f6 Mon Sep 17 00:00:00 2001 From: Tijmen van den Heuvel Date: Wed, 17 Jan 2024 20:13:00 +0100 Subject: [PATCH 078/189] Hardware accelerated implementation of Quaternion multiply (#96624) * Vector based implementation of Quaternion multiply I saw on the Discord that Quaternion multiply wasnt yet vectorised, this PR adds that. The non accelerated path is the exact same as before, and the accelerated path appears to return the same results. * Convert tabs to spaces * Update src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs Copy paste error! Co-authored-by: Austin Wise * Even faster version Tanner provided an even quicker version. I've changed it to use GetElementUnsafe as i saw other API's doing the same, and we can reasonably argue these fields to exist. * Update src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs Co-authored-by: Tanner Gooding * Fix incorrect non-hardware accelerated version --------- Co-authored-by: Austin Wise Co-authored-by: Tanner Gooding --- .../src/System/Numerics/Quaternion.cs | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs index 594491fcd218d4..ecebdb1b3d42f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs @@ -188,31 +188,45 @@ public readonly bool IsIdentity /// The method defines the operation of the multiplication operator for objects. public static Quaternion operator *(Quaternion value1, Quaternion value2) { - Quaternion ans; + if (Vector128.IsHardwareAccelerated) + { + var left = value1.AsVector128(); + var right = value2.AsVector128(); + + var result = right * left.GetElementUnsafe(3); + result += (Vector128.Shuffle(right, Vector128.Create(3, 2, 1, 0)) * left.GetElementUnsafe(0)) * Vector128.Create(+1.0f, -1.0f, +1.0f, -1.0f); + result += (Vector128.Shuffle(right, Vector128.Create(2, 3, 0, 1)) * left.GetElementUnsafe(1)) * Vector128.Create(+1.0f, +1.0f, -1.0f, -1.0f); + result += (Vector128.Shuffle(right, Vector128.Create(1, 0, 3, 2)) * left.GetElementUnsafe(2)) * Vector128.Create(-1.0f, +1.0f, +1.0f, -1.0f); + return Unsafe.BitCast, Quaternion>(result); + } + else + { + Quaternion ans; - float q1x = value1.X; - float q1y = value1.Y; - float q1z = value1.Z; - float q1w = value1.W; + float q1x = value1.X; + float q1y = value1.Y; + float q1z = value1.Z; + float q1w = value1.W; - float q2x = value2.X; - float q2y = value2.Y; - float q2z = value2.Z; - float q2w = value2.W; + float q2x = value2.X; + float q2y = value2.Y; + float q2z = value2.Z; + float q2w = value2.W; - // cross(av, bv) - float cx = q1y * q2z - q1z * q2y; - float cy = q1z * q2x - q1x * q2z; - float cz = q1x * q2y - q1y * q2x; + // cross(av, bv) + float cx = q1y * q2z - q1z * q2y; + float cy = q1z * q2x - q1x * q2z; + float cz = q1x * q2y - q1y * q2x; - float dot = q1x * q2x + q1y * q2y + q1z * q2z; + float dot = q1x * q2x + q1y * q2y + q1z * q2z; - ans.X = q1x * q2w + q2x * q1w + cx; - ans.Y = q1y * q2w + q2y * q1w + cy; - ans.Z = q1z * q2w + q2z * q1w + cz; - ans.W = q1w * q2w - dot; + ans.X = q1x * q2w + q2x * q1w + cx; + ans.Y = q1y * q2w + q2y * q1w + cy; + ans.Z = q1z * q2w + q2z * q1w + cz; + ans.W = q1w * q2w - dot; - return ans; + return ans; + } } /// Returns the quaternion that results from scaling all the components of a specified quaternion by a scalar factor. From 3e7bdb53de424107febec5129be749728ceb739c Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 17 Jan 2024 20:21:16 +0100 Subject: [PATCH 079/189] JIT: Clean up unrolling a bit (#97100) Factor out a large part of `optUnrollLoops` to an `optTryUnrollLoop` method to avoid some goto and to separate the loop/closure iteration from the actual unrolling of the individual loops. --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/optimizer.cpp | 943 +++++++++++++++++----------------- 2 files changed, 475 insertions(+), 469 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 960d6becf38ab4..c08f99c396c0b3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6818,6 +6818,7 @@ class Compiler PhaseStatus optCloneLoops(); void optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context); PhaseStatus optUnrollLoops(); // Unrolls loops (needs to have cost info) + bool optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR); void optRemoveRedundantZeroInits(); PhaseStatus optIfConversion(); // If conversion diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index b7679a56f67861..30b3fb845b6344 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -3415,10 +3415,99 @@ PhaseStatus Compiler::optUnrollLoops() // Look for loop unrolling candidates - bool change = false; + int unrollCount = 0; bool anyIRchange = false; - INDEBUG(int unrollCount = 0); // count of loops unrolled + int passes = 0; + + while (true) + { + // We track loops for which we unrolled a descendant loop. Since unrolling + // introduces/removes blocks, we retry unrolling for the parent loops + // separately, to avoid having to maintain the removed/added blocks. + BitVecTraits loopTraits((unsigned)m_loops->NumLoops(), this); + BitVec loopsWithUnrolledDescendant(BitVecOps::MakeEmpty(&loopTraits)); + + // Visit loops in post order (inner loops before outer loops). + for (FlowGraphNaturalLoop* loop : m_loops->InPostOrder()) + { + if (BitVecOps::IsMember(&loopTraits, loopsWithUnrolledDescendant, loop->GetIndex())) + { + continue; + } + + if (!optTryUnrollLoop(loop, &anyIRchange)) + { + continue; + } + + unrollCount++; + + // Mark in all ancestors now that one of their descendant loops was + // unrolled to indicate that the set of loop blocks changed. + for (FlowGraphNaturalLoop* ancestor = loop->GetParent(); ancestor != nullptr; + ancestor = ancestor->GetParent()) + { + BitVecOps::AddElemD(&loopTraits, loopsWithUnrolledDescendant, ancestor->GetIndex()); + } + } + + if ((unrollCount == 0) || BitVecOps::IsEmpty(&loopTraits, loopsWithUnrolledDescendant) || (passes >= 10)) + { + break; + } + + JITDUMP("A nested loop was unrolled. Doing another pass (pass %d)\n", passes + 1); + fgRenumberBlocks(); + fgInvalidateDfsTree(); + m_dfsTree = fgComputeDfs(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + passes++; + } + + if (unrollCount > 0) + { + assert(anyIRchange); + +#ifdef DEBUG + if (verbose) + { + printf("\nFinished unrolling %d loops in %d passes", unrollCount, passes); + printf("\n"); + } +#endif // DEBUG + + // We left the old loops unreachable as part of unrolling, so get rid of + // those blocks now. + fgDfsBlocksAndRemove(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + + fgRenumberBlocks(); + + DBEXEC(verbose, fgDispBasicBlocks()); + } + +#ifdef DEBUG + fgDebugCheckBBlist(true); +#endif // DEBUG + + return anyIRchange ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; +} + +//----------------------------------------------------------------------------- +// optTryUnrollLoop: Do legality and profitability checks and try to unroll a +// single loop. +// +// Parameters: +// loop - The loop to try unrolling +// changedIR - [out] Whether or not the IR was changed. Can be true even if +// the function returns false. +// +// Returns: +// True if the loop was unrolled, in which case the flow graph was changed. +// +bool Compiler::optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR) +{ static const unsigned ITER_LIMIT[COUNT_OPT_CODE + 1] = { 10, // BLENDED_CODE 0, // SMALL_CODE @@ -3448,561 +3537,477 @@ PhaseStatus Compiler::optUnrollLoops() assert(UNROLL_LIMIT_SZ[SMALL_CODE] == 0); assert(UNROLL_LIMIT_SZ[COUNT_OPT_CODE] == 0); - int passes = 0; -RETRY_UNROLL: - BitVecTraits loopTraits((unsigned)m_loops->NumLoops(), this); - // We track loops for which we unrolled a descendant loop. Since unrolling - // introduces/removes blocks, we retry unrolling for the parent loops - // separately, to avoid having to maintain the removed/added blocks. - BitVec loopsWithUnrolledDescendant(BitVecOps::MakeEmpty(&loopTraits)); - - // Visit loops in post order (inner loops before outer loops). - for (FlowGraphNaturalLoop* loop : m_loops->InPostOrder()) + NaturalLoopIterInfo iterInfo; + if (!loop->AnalyzeIteration(&iterInfo)) { - if (BitVecOps::IsMember(&loopTraits, loopsWithUnrolledDescendant, loop->GetIndex())) - { - continue; - } + return false; + } - NaturalLoopIterInfo iterInfo; - if (!loop->AnalyzeIteration(&iterInfo)) - { - continue; - } + // Check for required flags: + // HasConstInit - required because this transform only handles full unrolls + // HasConstLimit - required because this transform only handles full unrolls + if (!iterInfo.HasConstInit || !iterInfo.HasConstLimit) + { + // Don't print to the JitDump about this common case. + return false; + } - // Check for required flags: - // HasConstInit - required because this transform only handles full unrolls - // HasConstLimit - required because this transform only handles full unrolls - if (!iterInfo.HasConstInit || !iterInfo.HasConstLimit) - { - // Don't print to the JitDump about this common case. - continue; - } + // Get the loop data: + // - initial constant + // - limit constant + // - iterator + // - iterator increment + // - increment operation type (i.e. ADD, SUB, etc...) + // - loop test type (i.e. GT_GE, GT_LT, etc...) - // Get the loop data: - // - initial constant - // - limit constant - // - iterator - // - iterator increment - // - increment operation type (i.e. ADD, SUB, etc...) - // - loop test type (i.e. GT_GE, GT_LT, etc...) - - BasicBlock* initBlock = iterInfo.InitBlock; - int lbeg = iterInfo.ConstInitValue; - int llim = iterInfo.ConstLimit(); - genTreeOps testOper = iterInfo.TestOper(); - unsigned lvar = iterInfo.IterVar; - int iterInc = iterInfo.IterConst(); - genTreeOps iterOper = iterInfo.IterOper(); - var_types iterOperType = iterInfo.IterOperType(); - bool unsTest = (iterInfo.TestTree->gtFlags & GTF_UNSIGNED) != 0; - - assert(!lvaGetDesc(lvar)->IsAddressExposed()); - assert(!lvaGetDesc(lvar)->lvIsStructField); - - // Locate/initialize the increment/test statements. - Statement* initStmt = initBlock->lastStmt(); - noway_assert((initStmt != nullptr) && (initStmt->GetNextStmt() == nullptr)); - - bool dupCond = false; - if (initStmt->GetRootNode()->OperIs(GT_JTRUE)) - { - // Must be a duplicated loop condition. + BasicBlock* initBlock = iterInfo.InitBlock; + int lbeg = iterInfo.ConstInitValue; + int llim = iterInfo.ConstLimit(); + genTreeOps testOper = iterInfo.TestOper(); + unsigned lvar = iterInfo.IterVar; + int iterInc = iterInfo.IterConst(); + genTreeOps iterOper = iterInfo.IterOper(); + var_types iterOperType = iterInfo.IterOperType(); + bool unsTest = (iterInfo.TestTree->gtFlags & GTF_UNSIGNED) != 0; - dupCond = true; - initStmt = initStmt->GetPrevStmt(); - noway_assert(initStmt != nullptr); - } + assert(!lvaGetDesc(lvar)->IsAddressExposed()); + assert(!lvaGetDesc(lvar)->lvIsStructField); - // Find the number of iterations - the function returns false if not a constant number. - unsigned totalIter; - if (!optComputeLoopRep(lbeg, llim, iterInc, iterOper, iterOperType, testOper, unsTest, dupCond, &totalIter)) - { - JITDUMP("Failed to unroll loop " FMT_LP ": not a constant iteration count\n", loop->GetIndex()); - continue; - } + // Locate/initialize the increment/test statements. + Statement* initStmt = initBlock->lastStmt(); + noway_assert((initStmt != nullptr) && (initStmt->GetNextStmt() == nullptr)); - // Forget it if there are too many repetitions or not a constant loop. + bool dupCond = false; + if (initStmt->GetRootNode()->OperIs(GT_JTRUE)) + { + // Must be a duplicated loop condition. - if (totalIter > iterLimit) - { - JITDUMP("Failed to unroll loop " FMT_LP ": too many iterations (%d > %d) (heuristic)\n", loop->GetIndex(), - totalIter, iterLimit); - continue; - } + dupCond = true; + initStmt = initStmt->GetPrevStmt(); + noway_assert(initStmt != nullptr); + } - int unrollLimitSz = UNROLL_LIMIT_SZ[compCodeOpt()]; + // Find the number of iterations - the function returns false if not a constant number. + unsigned totalIter; + if (!optComputeLoopRep(lbeg, llim, iterInc, iterOper, iterOperType, testOper, unsTest, dupCond, &totalIter)) + { + JITDUMP("Failed to unroll loop " FMT_LP ": not a constant iteration count\n", loop->GetIndex()); + return false; + } - if (INDEBUG(compStressCompile(STRESS_UNROLL_LOOPS, 50) ||) false) - { - // In stress mode, quadruple the size limit, and drop - // the restriction that loop limit must be vector element count. - unrollLimitSz *= 4; - } - else if (totalIter <= 1) - { - // No limit for single iteration loops - // If there is no iteration (totalIter == 0), we will remove the loop body entirely. - unrollLimitSz = INT_MAX; - } - else if (totalIter <= opts.compJitUnrollLoopMaxIterationCount) - { - // We can unroll this - } - else if (iterInfo.HasSimdLimit) - { - // We can unroll this - } - else - { - JITDUMP("Failed to unroll loop " FMT_LP ": insufficiently simple loop (heuristic)\n", loop->GetIndex()); - continue; - } + // Forget it if there are too many repetitions or not a constant loop. - GenTree* incr = iterInfo.IterTree; + if (totalIter > iterLimit) + { + JITDUMP("Failed to unroll loop " FMT_LP ": too many iterations (%d > %d) (heuristic)\n", loop->GetIndex(), + totalIter, iterLimit); + return false; + } - // Don't unroll loops we don't understand. - if (!incr->OperIs(GT_STORE_LCL_VAR)) - { - JITDUMP("Failed to unroll loop " FMT_LP ": unknown increment op (%s)\n", loop->GetIndex(), - GenTree::OpName(incr->gtOper)); - continue; - } - incr = incr->AsLclVar()->Data(); + int unrollLimitSz = UNROLL_LIMIT_SZ[compCodeOpt()]; - GenTree* init = initStmt->GetRootNode(); + if (INDEBUG(compStressCompile(STRESS_UNROLL_LOOPS, 50) ||) false) + { + // In stress mode, quadruple the size limit, and drop + // the restriction that loop limit must be vector element count. + unrollLimitSz *= 4; + } + else if (totalIter <= 1) + { + // No limit for single iteration loops + // If there is no iteration (totalIter == 0), we will remove the loop body entirely. + unrollLimitSz = INT_MAX; + } + else if (totalIter <= opts.compJitUnrollLoopMaxIterationCount) + { + // We can unroll this + } + else if (iterInfo.HasSimdLimit) + { + // We can unroll this + } + else + { + JITDUMP("Failed to unroll loop " FMT_LP ": insufficiently simple loop (heuristic)\n", loop->GetIndex()); + return false; + } - // Make sure everything looks ok. - assert((iterInfo.TestBlock != nullptr) && iterInfo.TestBlock->KindIs(BBJ_COND)); + GenTree* incr = iterInfo.IterTree; - // clang-format off - if (!init->OperIs(GT_STORE_LCL_VAR) || - (init->AsLclVar()->GetLclNum() != lvar) || - !init->AsLclVar()->Data()->IsCnsIntOrI() || - (init->AsLclVar()->Data()->AsIntCon()->gtIconVal != lbeg) || + // Don't unroll loops we don't understand. + if (!incr->OperIs(GT_STORE_LCL_VAR)) + { + JITDUMP("Failed to unroll loop " FMT_LP ": unknown increment op (%s)\n", loop->GetIndex(), + GenTree::OpName(incr->gtOper)); + return false; + } + incr = incr->AsLclVar()->Data(); - !((incr->gtOper == GT_ADD) || (incr->gtOper == GT_SUB)) || - (incr->AsOp()->gtOp1->gtOper != GT_LCL_VAR) || - (incr->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum() != lvar) || - (incr->AsOp()->gtOp2->gtOper != GT_CNS_INT) || - (incr->AsOp()->gtOp2->AsIntCon()->gtIconVal != iterInc) || + GenTree* init = initStmt->GetRootNode(); - (iterInfo.TestBlock->lastStmt()->GetRootNode()->gtGetOp1() != iterInfo.TestTree)) - { - noway_assert(!"Bad precondition in Compiler::optUnrollLoops()"); - continue; - } - // clang-format on + // Make sure everything looks ok. + assert((iterInfo.TestBlock != nullptr) && iterInfo.TestBlock->KindIs(BBJ_COND)); - // After this point, assume we've changed the IR. In particular, we call gtSetStmtInfo() which - // can modify the IR. We may still fail to unroll if the EH region conditions don't hold, if - // the size heuristics don't succeed, or if cloning any individual block fails. - anyIRchange = true; + // clang-format off + if (!init->OperIs(GT_STORE_LCL_VAR) || + (init->AsLclVar()->GetLclNum() != lvar) || + !init->AsLclVar()->Data()->IsCnsIntOrI() || + (init->AsLclVar()->Data()->AsIntCon()->gtIconVal != lbeg) || - // Heuristic: Estimated cost in code size of the unrolled loop. + !((incr->gtOper == GT_ADD) || (incr->gtOper == GT_SUB)) || + (incr->AsOp()->gtOp1->gtOper != GT_LCL_VAR) || + (incr->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum() != lvar) || + (incr->AsOp()->gtOp2->gtOper != GT_CNS_INT) || + (incr->AsOp()->gtOp2->AsIntCon()->gtIconVal != iterInc) || - { - ClrSafeInt loopCostSz; // Cost is size of one iteration + (iterInfo.TestBlock->lastStmt()->GetRootNode()->gtGetOp1() != iterInfo.TestTree)) + { + noway_assert(!"Bad precondition in Compiler::optUnrollLoops()"); + return false; + } + // clang-format on - BasicBlockVisit result = loop->VisitLoopBlocksReversePostOrder([=, &loopCostSz](BasicBlock* block) { + // After this point, assume we've changed the IR. In particular, we call gtSetStmtInfo() which + // can modify the IR. We may still fail to unroll if the EH region conditions don't hold, or if + // the size heuristics don't succeed. + *changedIR = true; - if (!BasicBlock::sameEHRegion(block, loop->GetHeader())) - { - // Unrolling would require cloning EH regions - // Note that only non-funclet model (x86) could actually have a loop including a handler - // but not it's corresponding `try`, if its `try` was moved due to being marked "rare". - JITDUMP("Failed to unroll loop " FMT_LP ": EH constraint\n", loop->GetIndex()); - return BasicBlockVisit::Abort; - } + // Heuristic: Estimated cost in code size of the unrolled loop. - for (Statement* const stmt : block->Statements()) - { - gtSetStmtInfo(stmt); - loopCostSz += stmt->GetCostSz(); - } + ClrSafeInt loopCostSz; // Cost is size of one iteration - return BasicBlockVisit::Continue; - }); + BasicBlockVisit result = loop->VisitLoopBlocksReversePostOrder([=, &loopCostSz](BasicBlock* block) { - if (result == BasicBlockVisit::Abort) - { - continue; - } + if (!BasicBlock::sameEHRegion(block, loop->GetHeader())) + { + // Unrolling would require cloning EH regions + // Note that only non-funclet model (x86) could actually have a loop including a handler + // but not it's corresponding `try`, if its `try` was moved due to being marked "rare". + JITDUMP("Failed to unroll loop " FMT_LP ": EH constraint\n", loop->GetIndex()); + return BasicBlockVisit::Abort; + } -#ifdef DEBUG - // With the EH constraint above verified it is not possible for - // BBJ_RETURN blocks to be part of the loop; a BBJ_RETURN block can - // only be part of the loop if its exceptional flow can reach the - // header, but that would require the handler to also be part of - // the loop, which guarantees that the loop contains two distinct - // EH regions. - loop->VisitLoopBlocks([](BasicBlock* block) { - assert(!block->KindIs(BBJ_RETURN)); - return BasicBlockVisit::Continue; - }); -#endif + for (Statement* const stmt : block->Statements()) + { + gtSetStmtInfo(stmt); + loopCostSz += stmt->GetCostSz(); + } - // Compute the estimated increase in code size for the unrolled loop. + return BasicBlockVisit::Continue; + }); - ClrSafeInt fixedLoopCostSz(8); + if (result == BasicBlockVisit::Abort) + { + return false; + } - ClrSafeInt unrollCostSz = ClrSafeInt(loopCostSz * ClrSafeInt(totalIter)) - - ClrSafeInt(loopCostSz + fixedLoopCostSz); +#ifdef DEBUG + // With the EH constraint above verified it is not possible for + // BBJ_RETURN blocks to be part of the loop; a BBJ_RETURN block can + // only be part of the loop if its exceptional flow can reach the + // header, but that would require the handler to also be part of + // the loop, which guarantees that the loop contains two distinct + // EH regions. + loop->VisitLoopBlocks([](BasicBlock* block) { + assert(!block->KindIs(BBJ_RETURN)); + return BasicBlockVisit::Continue; + }); +#endif - // Don't unroll if too much code duplication would result. + // Compute the estimated increase in code size for the unrolled loop. - if (unrollCostSz.IsOverflow() || (unrollCostSz.Value() > unrollLimitSz)) - { - JITDUMP("Failed to unroll loop " FMT_LP ": size constraint (%d > %d) (heuristic)\n", loop->GetIndex(), - unrollCostSz.Value(), unrollLimitSz); - continue; - } + ClrSafeInt fixedLoopCostSz(8); - // Looks like a good idea to unroll this loop, let's do it! - CLANG_FORMAT_COMMENT_ANCHOR; + ClrSafeInt unrollCostSz = + ClrSafeInt(loopCostSz * ClrSafeInt(totalIter)) - ClrSafeInt(loopCostSz + fixedLoopCostSz); - JITDUMP("\nUnrolling loop " FMT_LP " unrollCostSz = %d\n", loop->GetIndex(), unrollCostSz.Value()); - JITDUMPEXEC(FlowGraphNaturalLoop::Dump(loop)); - } + // Don't unroll if too much code duplication would result. - // Create the unrolled loop statement list. - { - // We unroll a loop focused around the test and IV that was - // identified by FlowGraphNaturalLoop::AnalyzeIteration. Note that: - // - // * The loop can have multiple exits. The exit guarded on the IV - // is the one we can optimize away when we unroll, since we know - // the value of the IV in each iteration. The other exits will - // remain in place in each iteration. - // - // * The loop can have multiple backedges. Often, there is a - // single backedge that becomes statically unreachable when we - // optimize the exit guarded on the IV. In that case the loop - // structure disappears. However, if there were multiple backedges, - // the loop structure can remain in each unrolled iteration. - // - // * The loop being unrolled can also have nested loops, which will - // be duplicated for each unrolled iteration. - // - // * Unrolling a loop creates or removes basic blocks, depending on - // whether the iter count is 0. When nested loops are unrolled, - // instead of trying to maintain the new right set of loop blocks - // that exist in all ancestor loops, we skip unrolling for all - // ancestor loops and instead recompute the loop structure and - // retry unrolling. It is rare to have multiple nested unrollings - // of loops, so this is not a TP issue. - - BlockToBlockMap blockMap(getAllocator(CMK_LoopUnroll)); - - BasicBlock* bottom = loop->GetLexicallyBottomMostBlock(); - BasicBlock* insertAfter = bottom; - BasicBlock* prevTestBlock = nullptr; - unsigned iterToUnroll = totalIter; // The number of iterations left to unroll - - // Find the exit block of the IV test first. We need to do that - // here since it may have implicit fallthrough that we'll change - // below. - BasicBlock* exiting = iterInfo.TestBlock; - assert(exiting->KindIs(BBJ_COND)); - assert(loop->ContainsBlock(exiting->GetTrueTarget()) != loop->ContainsBlock(exiting->GetFalseTarget())); - BasicBlock* exit = - loop->ContainsBlock(exiting->GetTrueTarget()) ? exiting->GetFalseTarget() : exiting->GetTrueTarget(); - - // If the bottom block falls out of the loop, then insert an - // explicit block to branch around the unrolled iterations we are - // going to create. - if (bottom->KindIs(BBJ_COND)) - { - // TODO-NoFallThrough: Shouldn't need new BBJ_ALWAYS block once bbFalseTarget can diverge from bbNext - BasicBlock* bottomNext = bottom->Next(); - assert(bottom->FalseTargetIs(bottomNext)); - JITDUMP("Create branch around unrolled loop\n"); - BasicBlock* bottomRedirBlk = fgNewBBafter(BBJ_ALWAYS, bottom, /*extendRegion*/ true, bottomNext); - JITDUMP("Adding " FMT_BB " after " FMT_BB "\n", bottomRedirBlk->bbNum, bottom->bbNum); - - bottom->SetFalseTarget(bottomRedirBlk); - fgAddRefPred(bottomRedirBlk, bottom); - JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomRedirBlk->bbNum); - fgReplacePred(bottomNext, bottom, bottomRedirBlk); - JITDUMP("Replace " FMT_BB " -> " FMT_BB " with " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, - bottomNext->bbNum, bottomRedirBlk->bbNum, bottomNext->bbNum); - - insertAfter = bottomRedirBlk; - } - - for (int lval = lbeg; iterToUnroll > 0; iterToUnroll--) - { - BasicBlock* testBlock = nullptr; - loop->VisitLoopBlocksLexical([&](BasicBlock* block) { - - // Don't set a jump target for now. - // BasicBlock::CopyTarget() will fix the jump kind/target in the loop below. - BasicBlock* newBlock = fgNewBBafter(BBJ_ALWAYS, insertAfter, /*extendRegion*/ true); - insertAfter = newBlock; - - blockMap.Set(block, newBlock, BlockToBlockMap::Overwrite); - - // Now clone block state and statements from `from` block to `to` block. - // - BasicBlock::CloneBlockState(this, newBlock, block, lvar, lval); + if (unrollCostSz.IsOverflow() || (unrollCostSz.Value() > unrollLimitSz)) + { + JITDUMP("Failed to unroll loop " FMT_LP ": size constraint (%d > %d) (heuristic)\n", loop->GetIndex(), + unrollCostSz.Value(), unrollLimitSz); + return false; + } - newBlock->RemoveFlags(BBF_OLD_LOOP_HEADER_QUIRK); + // Looks like a good idea to unroll this loop, let's do it! + JITDUMP("\nUnrolling loop " FMT_LP " unrollCostSz = %d\n", loop->GetIndex(), unrollCostSz.Value()); + JITDUMPEXEC(FlowGraphNaturalLoop::Dump(loop)); - // Block weight should no longer have the loop multiplier - // - // Note this is not quite right, as we may not have upscaled by this amount - // and we might not have upscaled at all, if we had profile data. - // - newBlock->scaleBBWeight(1.0 / BB_LOOP_WEIGHT_SCALE); + // We unroll a loop focused around the test and IV that was + // identified by FlowGraphNaturalLoop::AnalyzeIteration. Note that: + // + // * The loop can have multiple exits. The exit guarded on the IV + // is the one we can optimize away when we unroll, since we know + // the value of the IV in each iteration. The other exits will + // remain in place in each iteration. + // + // * The loop can have multiple backedges. Often, there is a + // single backedge that becomes statically unreachable when we + // optimize the exit guarded on the IV. In that case the loop + // structure disappears. However, if there were multiple backedges, + // the loop structure can remain in each unrolled iteration. + // + // * The loop being unrolled can also have nested loops, which will + // be duplicated for each unrolled iteration. + // + // * Unrolling a loop creates or removes basic blocks, depending on + // whether the iter count is 0. When nested loops are unrolled, + // instead of trying to maintain the new right set of loop blocks + // that exist in all ancestor loops, we skip unrolling for all + // ancestor loops and instead recompute the loop structure and + // retry unrolling. It is rare to have multiple nested unrollings + // of loops, so this is not a TP issue. + + BlockToBlockMap blockMap(getAllocator(CMK_LoopUnroll)); + + BasicBlock* bottom = loop->GetLexicallyBottomMostBlock(); + BasicBlock* insertAfter = bottom; + BasicBlock* prevTestBlock = nullptr; + unsigned iterToUnroll = totalIter; // The number of iterations left to unroll + + // Find the exit block of the IV test first. We need to do that + // here since it may have implicit fallthrough that we'll change + // below. + BasicBlock* exiting = iterInfo.TestBlock; + assert(exiting->KindIs(BBJ_COND)); + assert(loop->ContainsBlock(exiting->GetTrueTarget()) != loop->ContainsBlock(exiting->GetFalseTarget())); + BasicBlock* exit = + loop->ContainsBlock(exiting->GetTrueTarget()) ? exiting->GetFalseTarget() : exiting->GetTrueTarget(); + + // If the bottom block falls out of the loop, then insert an + // explicit block to branch around the unrolled iterations we are + // going to create. + if (bottom->KindIs(BBJ_COND)) + { + // TODO-NoFallThrough: Shouldn't need new BBJ_ALWAYS block once bbFalseTarget can diverge from bbNext + BasicBlock* bottomNext = bottom->Next(); + assert(bottom->FalseTargetIs(bottomNext)); + JITDUMP("Create branch around unrolled loop\n"); + BasicBlock* bottomRedirBlk = fgNewBBafter(BBJ_ALWAYS, bottom, /*extendRegion*/ true, bottomNext); + JITDUMP("Adding " FMT_BB " after " FMT_BB "\n", bottomRedirBlk->bbNum, bottom->bbNum); - // Jump dests are set in a post-pass; make sure CloneBlockState hasn't tried to set them. - assert(newBlock->KindIs(BBJ_ALWAYS)); - assert(!newBlock->HasInitializedTarget()); + bottom->SetFalseTarget(bottomRedirBlk); + fgAddRefPred(bottomRedirBlk, bottom); + JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomRedirBlk->bbNum); + fgReplacePred(bottomNext, bottom, bottomRedirBlk); + JITDUMP("Replace " FMT_BB " -> " FMT_BB " with " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomNext->bbNum, + bottomRedirBlk->bbNum, bottomNext->bbNum); - if (block == iterInfo.TestBlock) - { - // Remove the test; we're doing a full unroll. - - Statement* testCopyStmt = newBlock->lastStmt(); - GenTree* testCopyExpr = testCopyStmt->GetRootNode(); - assert(testCopyExpr->gtOper == GT_JTRUE); - GenTree* sideEffList = nullptr; - gtExtractSideEffList(testCopyExpr, &sideEffList, GTF_SIDE_EFFECT | GTF_ORDER_SIDEEFF); - if (sideEffList == nullptr) - { - fgRemoveStmt(newBlock, testCopyStmt); - } - else - { - testCopyStmt->SetRootNode(sideEffList); - } + insertAfter = bottomRedirBlk; + } - // Save the test block of the previously unrolled - // iteration, so that we can redirect it when we create - // the next iteration (or to the exit for the last - // iteration). - assert(testBlock == nullptr); - testBlock = newBlock; - } - else if (block->bbFallsThrough() && !loop->ContainsBlock(block->Next())) - { - assert(block->KindIs(BBJ_COND) && "Cannot handle fallthrough for non BBJ_COND block"); - // Handle fallthrough. - // TODO-Quirk: Skip empty blocks and go directly to their destination. - BasicBlock* targetBlk = block->Next(); - if (targetBlk->KindIs(BBJ_ALWAYS) && targetBlk->isEmpty()) - targetBlk = targetBlk->GetTarget(); - - BasicBlock* newRedirBlk = - fgNewBBafter(BBJ_ALWAYS, insertAfter, /* extendRegion */ true, targetBlk); - newRedirBlk->copyEHRegion(insertAfter); - newRedirBlk->bbWeight = block->Next()->bbWeight; - newRedirBlk->CopyFlags(block->Next(), BBF_RUN_RARELY | BBF_PROF_WEIGHT); - newRedirBlk->scaleBBWeight(1.0 / BB_LOOP_WEIGHT_SCALE); - - fgAddRefPred(targetBlk, newRedirBlk); - insertAfter = newRedirBlk; - } + for (int lval = lbeg; iterToUnroll > 0; iterToUnroll--) + { + BasicBlock* testBlock = nullptr; + loop->VisitLoopBlocksLexical([&](BasicBlock* block) { - return BasicBlockVisit::Continue; - }); + // Don't set a jump target for now. + // BasicBlock::CopyTarget() will fix the jump kind/target in the loop below. + BasicBlock* newBlock = fgNewBBafter(BBJ_ALWAYS, insertAfter, /*extendRegion*/ true); + insertAfter = newBlock; - assert(testBlock != nullptr); + blockMap.Set(block, newBlock, BlockToBlockMap::Overwrite); - // Now redirect any branches within the newly-cloned iteration. - loop->VisitLoopBlocks([=, &blockMap](BasicBlock* block) { - // Do not include the test block; we will redirect it on - // the next iteration or after the loop. - if (block == iterInfo.TestBlock) - { - return BasicBlockVisit::Continue; - } + // Now clone block state and statements from `from` block to `to` block. + // + BasicBlock::CloneBlockState(this, newBlock, block, lvar, lval); - // Jump kind/target should not be set yet - BasicBlock* newBlock = blockMap[block]; - assert(!newBlock->HasInitializedTarget()); + newBlock->RemoveFlags(BBF_OLD_LOOP_HEADER_QUIRK); - // Now copy the jump kind/target - newBlock->CopyTarget(this, block); - optRedirectBlock(newBlock, &blockMap, RedirectBlockOption::AddToPredLists); + // Block weight should no longer have the loop multiplier + // + // Note this is not quite right, as we may not have upscaled by this amount + // and we might not have upscaled at all, if we had profile data. + // + newBlock->scaleBBWeight(1.0 / BB_LOOP_WEIGHT_SCALE); + + // Jump dests are set in a post-pass; make sure CloneBlockState hasn't tried to set them. + assert(newBlock->KindIs(BBJ_ALWAYS)); + assert(!newBlock->HasInitializedTarget()); - return BasicBlockVisit::Continue; - }); + if (block == iterInfo.TestBlock) + { + // Remove the test; we're doing a full unroll. - // Redirect previous iteration (or entry) to this iteration. - if (prevTestBlock != nullptr) + Statement* testCopyStmt = newBlock->lastStmt(); + GenTree* testCopyExpr = testCopyStmt->GetRootNode(); + assert(testCopyExpr->gtOper == GT_JTRUE); + GenTree* sideEffList = nullptr; + gtExtractSideEffList(testCopyExpr, &sideEffList, GTF_SIDE_EFFECT | GTF_ORDER_SIDEEFF); + if (sideEffList == nullptr) { - // Redirect exit edge from previous iteration to new entry. - assert(prevTestBlock->KindIs(BBJ_ALWAYS)); - BasicBlock* newHeader = blockMap[loop->GetHeader()]; - prevTestBlock->SetTarget(newHeader); - fgAddRefPred(newHeader, prevTestBlock); - - JITDUMP("Redirecting previously created exiting " FMT_BB " -> " FMT_BB - " (unrolled iteration header)\n", - prevTestBlock->bbNum, newHeader->bbNum); + fgRemoveStmt(newBlock, testCopyStmt); } else { - // Redirect all predecessors to the new one. - for (FlowEdge* enterEdge : loop->EntryEdges()) - { - BasicBlock* entering = enterEdge->getSourceBlock(); - JITDUMP("Redirecting " FMT_BB " -> " FMT_BB " to " FMT_BB " -> " FMT_BB "\n", entering->bbNum, - loop->GetHeader()->bbNum, entering->bbNum, blockMap[loop->GetHeader()]->bbNum); - assert(!entering->KindIs(BBJ_COND)); // Ensured by canonicalization - optRedirectBlock(entering, &blockMap, Compiler::RedirectBlockOption::UpdatePredLists); - } + testCopyStmt->SetRootNode(sideEffList); } - prevTestBlock = testBlock; + // Save the test block of the previously unrolled + // iteration, so that we can redirect it when we create + // the next iteration (or to the exit for the last + // iteration). + assert(testBlock == nullptr); + testBlock = newBlock; + } + else if (block->bbFallsThrough() && !loop->ContainsBlock(block->Next())) + { + assert(block->KindIs(BBJ_COND) && "Cannot handle fallthrough for non BBJ_COND block"); + // Handle fallthrough. + // TODO-Quirk: Skip empty blocks and go directly to their destination. + BasicBlock* targetBlk = block->Next(); + if (targetBlk->KindIs(BBJ_ALWAYS) && targetBlk->isEmpty()) + targetBlk = targetBlk->GetTarget(); - // update the new value for the unrolled iterator + BasicBlock* newRedirBlk = fgNewBBafter(BBJ_ALWAYS, insertAfter, /* extendRegion */ true, targetBlk); + newRedirBlk->copyEHRegion(insertAfter); + newRedirBlk->bbWeight = block->Next()->bbWeight; + newRedirBlk->CopyFlags(block->Next(), BBF_RUN_RARELY | BBF_PROF_WEIGHT); + newRedirBlk->scaleBBWeight(1.0 / BB_LOOP_WEIGHT_SCALE); - switch (iterOper) - { - case GT_ADD: - lval += iterInc; - break; - - case GT_SUB: - lval -= iterInc; - break; + fgAddRefPred(targetBlk, newRedirBlk); + insertAfter = newRedirBlk; + } - case GT_RSH: - case GT_LSH: - noway_assert(!"Unrolling not implemented for this loop iterator"); - goto DONE_LOOP; + return BasicBlockVisit::Continue; + }); - default: - noway_assert(!"Unknown operator for constant loop iterator"); - goto DONE_LOOP; - } - } + assert(testBlock != nullptr); - // If we get here, we successfully cloned all the blocks in the unrolled loop. - // Note we may not have done any cloning at all, if the loop iteration count was zero. - // Now redirect the last iteration to the real exit of the loop (or - // the entry to the exit if we unrolled 0 iterations). - if (prevTestBlock != nullptr) + // Now redirect any branches within the newly-cloned iteration. + loop->VisitLoopBlocks([=, &blockMap](BasicBlock* block) { + // Do not include the test block; we will redirect it on + // the next iteration or after the loop. + if (block == iterInfo.TestBlock) { - assert(prevTestBlock->KindIs(BBJ_ALWAYS)); - prevTestBlock->SetTarget(exit); - fgAddRefPred(exit, prevTestBlock); - JITDUMP("Redirecting final iteration exiting " FMT_BB " to original exit " FMT_BB "\n", - prevTestBlock->bbNum, exit->bbNum); + return BasicBlockVisit::Continue; } - else - { - blockMap.Set(loop->GetHeader(), exit, BlockToBlockMap::Overwrite); - for (FlowEdge* entryEdge : loop->EntryEdges()) - { - BasicBlock* entering = entryEdge->getSourceBlock(); - assert(!entering->KindIs(BBJ_COND)); // Ensured by canonicalization - optRedirectBlock(entering, &blockMap, Compiler::RedirectBlockOption::UpdatePredLists); - JITDUMP("Redirecting original entry " FMT_BB " -> " FMT_BB " to " FMT_BB " -> " FMT_BB "\n", - entering->bbNum, loop->GetHeader()->bbNum, entering->bbNum, exit->bbNum); - } - } + // Jump kind/target should not be set yet + BasicBlock* newBlock = blockMap[block]; + assert(!newBlock->HasInitializedTarget()); - // The old loop body is unreachable now. + // Now copy the jump kind/target + newBlock->CopyTarget(this, block); + optRedirectBlock(newBlock, &blockMap, RedirectBlockOption::AddToPredLists); - // Control will fall through from the initBlock to its successor, which is either - // the preheader HEAD (if it exists), or the now empty TOP (if totalIter == 0), - // or the first cloned top. - // - // If the initBlock is a BBJ_COND drop the condition (and make initBlock a BBJ_ALWAYS block). - // - // TODO: Isn't this missing validity checks? This seems dangerous. - // - if (initBlock->KindIs(BBJ_COND)) - { - assert(dupCond); - Statement* initBlockBranchStmt = initBlock->lastStmt(); - noway_assert(initBlockBranchStmt->GetRootNode()->OperIs(GT_JTRUE)); - fgRemoveStmt(initBlock, initBlockBranchStmt); - fgRemoveRefPred(initBlock->GetTrueTarget(), initBlock); - initBlock->SetKindAndTarget(BBJ_ALWAYS, initBlock->GetFalseTarget()); + return BasicBlockVisit::Continue; + }); - // TODO-NoFallThrough: If bbFalseTarget can diverge from bbNext, it may not make sense to set - // BBF_NONE_QUIRK - initBlock->SetFlags(BBF_NONE_QUIRK); - } - else + // Redirect previous iteration (or entry) to this iteration. + if (prevTestBlock != nullptr) + { + // Redirect exit edge from previous iteration to new entry. + assert(prevTestBlock->KindIs(BBJ_ALWAYS)); + BasicBlock* newHeader = blockMap[loop->GetHeader()]; + prevTestBlock->SetTarget(newHeader); + fgAddRefPred(newHeader, prevTestBlock); + + JITDUMP("Redirecting previously created exiting " FMT_BB " -> " FMT_BB " (unrolled iteration header)\n", + prevTestBlock->bbNum, newHeader->bbNum); + } + else + { + // Redirect all predecessors to the new one. + for (FlowEdge* enterEdge : loop->EntryEdges()) { - // the loop must execute - assert(!dupCond); - assert(totalIter > 0); - noway_assert(initBlock->KindIs(BBJ_ALWAYS)); + BasicBlock* entering = enterEdge->getSourceBlock(); + JITDUMP("Redirecting " FMT_BB " -> " FMT_BB " to " FMT_BB " -> " FMT_BB "\n", entering->bbNum, + loop->GetHeader()->bbNum, entering->bbNum, blockMap[loop->GetHeader()]->bbNum); + assert(!entering->KindIs(BBJ_COND)); // Ensured by canonicalization + optRedirectBlock(entering, &blockMap, Compiler::RedirectBlockOption::UpdatePredLists); } + } -#ifdef DEBUG - if (verbose) - { - printf("Whole unrolled loop:\n"); + prevTestBlock = testBlock; - gtDispTree(initStmt->GetRootNode()); - printf("\n"); - fgDumpTrees(bottom, insertAfter); - } -#endif // DEBUG + // update the new value for the unrolled iterator - // Remember that something has changed. - INDEBUG(++unrollCount); - change = true; + switch (iterOper) + { + case GT_ADD: + lval += iterInc; + break; - for (FlowGraphNaturalLoop* ancestor = loop->GetParent(); ancestor != nullptr; - ancestor = ancestor->GetParent()) - { - BitVecOps::AddElemD(&loopTraits, loopsWithUnrolledDescendant, ancestor->GetIndex()); - } - } + case GT_SUB: + lval -= iterInc; + break; - DONE_LOOP:; + default: + unreached(); + } } - if (change && !BitVecOps::IsEmpty(&loopTraits, loopsWithUnrolledDescendant) && (passes < 10)) + // If we get here, we successfully cloned all the blocks in the unrolled loop. + // Note we may not have done any cloning at all, if the loop iteration count was zero. + // Now redirect the last iteration to the real exit of the loop (or + // the entry to the exit if we unrolled 0 iterations). + if (prevTestBlock != nullptr) { - fgRenumberBlocks(); // For proper lexical visit - fgInvalidateDfsTree(); - m_dfsTree = fgComputeDfs(); - m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); - passes++; - goto RETRY_UNROLL; + assert(prevTestBlock->KindIs(BBJ_ALWAYS)); + prevTestBlock->SetTarget(exit); + fgAddRefPred(exit, prevTestBlock); + JITDUMP("Redirecting final iteration exiting " FMT_BB " to original exit " FMT_BB "\n", prevTestBlock->bbNum, + exit->bbNum); } - - if (change) + else { - assert(anyIRchange); - -#ifdef DEBUG - if (verbose) + blockMap.Set(loop->GetHeader(), exit, BlockToBlockMap::Overwrite); + for (FlowEdge* entryEdge : loop->EntryEdges()) { - printf("\nFinished unrolling %d loops", unrollCount); - printf("\n"); + BasicBlock* entering = entryEdge->getSourceBlock(); + assert(!entering->KindIs(BBJ_COND)); // Ensured by canonicalization + optRedirectBlock(entering, &blockMap, Compiler::RedirectBlockOption::UpdatePredLists); + + JITDUMP("Redirecting original entry " FMT_BB " -> " FMT_BB " to " FMT_BB " -> " FMT_BB "\n", + entering->bbNum, loop->GetHeader()->bbNum, entering->bbNum, exit->bbNum); } -#endif // DEBUG + } - // We left the old loop unreachable as part of unrolling, so get rid of - // those blocks now. - fgDfsBlocksAndRemove(); - m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + // The old loop body is unreachable now. - fgRenumberBlocks(); + // Control will fall through from the initBlock to its successor, which is either + // the preheader HEAD (if it exists), or the now empty TOP (if totalIter == 0), + // or the first cloned top. + // + // If the initBlock is a BBJ_COND drop the condition (and make initBlock a BBJ_ALWAYS block). + // + // TODO: Isn't this missing validity checks? This seems dangerous. + // + if (initBlock->KindIs(BBJ_COND)) + { + assert(dupCond); + Statement* initBlockBranchStmt = initBlock->lastStmt(); + noway_assert(initBlockBranchStmt->GetRootNode()->OperIs(GT_JTRUE)); + fgRemoveStmt(initBlock, initBlockBranchStmt); + fgRemoveRefPred(initBlock->GetTrueTarget(), initBlock); + initBlock->SetKindAndTarget(BBJ_ALWAYS, initBlock->GetFalseTarget()); - DBEXEC(verbose, fgDispBasicBlocks()); + // TODO-NoFallThrough: If bbFalseTarget can diverge from bbNext, it may not make sense to set + // BBF_NONE_QUIRK + initBlock->SetFlags(BBF_NONE_QUIRK); } else { - assert(unrollCount == 0); + // the loop must execute + assert(!dupCond); + assert(totalIter > 0); + noway_assert(initBlock->KindIs(BBJ_ALWAYS)); } #ifdef DEBUG - fgDebugCheckBBlist(true); + if (verbose) + { + printf("Whole unrolled loop:\n"); + + gtDispTree(initStmt->GetRootNode()); + printf("\n"); + fgDumpTrees(bottom->Next(), insertAfter); + } #endif // DEBUG - return anyIRchange ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; + return true; } Compiler::OptInvertCountTreeInfoType Compiler::optInvertCountTreeInfo(GenTree* tree) From 5ff11b139633f63c982f8b359e7b19eb1c4f2697 Mon Sep 17 00:00:00 2001 From: Jeremi Kurdek <59935235+jkurdek@users.noreply.github.com> Date: Wed, 17 Jan 2024 20:29:40 +0100 Subject: [PATCH 080/189] [Mono] Add amd64 intrinsics for Vector128 Abs (#97024) * [Mono] Add amd64 intrinsics for Vector128 Abs * added i64 pointer support * applied review suggestions * fixed types in non ssse3 fallback --- src/mono/mono/arch/amd64/amd64-codegen.h | 5 +++++ src/mono/mono/mini/cpu-amd64.mdesc | 1 + src/mono/mono/mini/mini-amd64.c | 19 +++++++++++++++++++ src/mono/mono/mini/simd-intrinsics.c | 21 +++++++++++++++++---- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/arch/amd64/amd64-codegen.h b/src/mono/mono/arch/amd64/amd64-codegen.h index e92018f97385d7..e20e43357cee6e 100644 --- a/src/mono/mono/arch/amd64/amd64-codegen.h +++ b/src/mono/mono/arch/amd64/amd64-codegen.h @@ -1194,6 +1194,11 @@ typedef union { #define amd64_sse_phaddw_reg_reg(inst, dreg, sreg) emit_sse_reg_reg_op4((inst), (dreg), (sreg), 0x66, 0x0f, 0x38, 0x01) #define amd64_sse_phaddd_reg_reg(inst, dreg, sreg) emit_sse_reg_reg_op4((inst), (dreg), (sreg), 0x66, 0x0f, 0x38, 0x02) #define amd64_sse_blendpd_reg_reg(inst,dreg,sreg,imm) emit_sse_reg_reg_op4_imm((inst), (dreg), (sreg), 0x66, 0x0f, 0x3a, 0x0d, (imm)) + +#define amd64_ssse3_pabsb_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x1c) +#define amd64_ssse3_pabsw_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x1d) +#define amd64_ssse3_pabsd_reg_reg(inst, dreg, reg) emit_sse_reg_reg_op4((inst), (dreg), (reg), 0x66, 0x0f, 0x38, 0x1e) + #define amd64_movq_reg_reg(inst,dreg,sreg) emit_sse_reg_reg ((inst), (dreg), (sreg), 0xf3, 0x0f, 0x7e) /* Generated from x86-codegen.h */ diff --git a/src/mono/mono/mini/cpu-amd64.mdesc b/src/mono/mono/mini/cpu-amd64.mdesc index 2658508e72b0d9..631b49b5aeea2e 100644 --- a/src/mono/mono/mini/cpu-amd64.mdesc +++ b/src/mono/mono/mini/cpu-amd64.mdesc @@ -843,6 +843,7 @@ ssse3_shuffle: dest:x src1:x src2:x len:6 clob:1 sse41_dpps_imm: dest:x src1:x src2:x len:7 clob:1 sse41_dppd_imm: dest:x src1:x src2:x len:7 clob:1 vector_andnot: dest:x src1:x src2:x len:7 clob:1 +vector_integer_abs: dest:x src1:x len:6 roundp: dest:x src1:x len:10 diff --git a/src/mono/mono/mini/mini-amd64.c b/src/mono/mono/mini/mini-amd64.c index e58703d3e2b649..c0f18b73e7e2d5 100644 --- a/src/mono/mono/mini/mini-amd64.c +++ b/src/mono/mono/mini/mini-amd64.c @@ -7607,6 +7607,25 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_SSSE3_SHUFFLE: amd64_sse_pshufb_reg_reg (code, ins->dreg, ins->sreg2); break; + case OP_VECTOR_IABS: + switch (ins->inst_c1) { + case MONO_TYPE_I1: + amd64_ssse3_pabsb_reg_reg(code, ins->dreg, ins->sreg1); + break; + case MONO_TYPE_I2: + amd64_ssse3_pabsw_reg_reg(code, ins->dreg, ins->sreg1); + break; + case MONO_TYPE_I4: +#if TARGET_SIZEOF_VOID_P == 4 + case MONO_TYPE_I: +#endif + amd64_ssse3_pabsd_reg_reg(code, ins->dreg, ins->sreg1); + break; + default: + g_assert_not_reached (); + break; + } + break; case OP_SSE41_ROUNDP: { if (ins->inst_c1 == MONO_TYPE_R8) amd64_sse_roundpd_reg_reg_imm (code, ins->dreg, ins->sreg1, ins->inst_c0); diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index eb1c1c687be5b4..3004bb6aa268c7 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -1475,10 +1475,23 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi ins->inst_c1 = arg0_type; return ins; } else { - if (!COMPILE_LLVM (cfg)) - // FIXME: - return NULL; - return emit_simd_ins_for_sig (cfg, klass, OP_VECTOR_IABS, -1, arg0_type, fsig, args); + if (COMPILE_LLVM (cfg)) + return emit_simd_ins_for_sig (cfg, klass, OP_VECTOR_IABS, -1, arg0_type, fsig, args); + + // SSSE3 does not support i64 + if (is_SIMD_feature_supported (cfg, MONO_CPU_X86_SSSE3) && + !(arg0_type == MONO_TYPE_I8 || (TARGET_SIZEOF_VOID_P == 8 && arg0_type == MONO_TYPE_I))) + return emit_simd_ins_for_sig (cfg, klass, OP_VECTOR_IABS, -1, arg0_type, fsig, args); + + MonoInst *zero = emit_xzero (cfg, klass); + MonoInst *neg = emit_simd_ins (cfg, klass, OP_XBINOP, zero->dreg, args [0]->dreg); + neg->inst_c0 = OP_ISUB; + neg->inst_c1 = arg0_type; + + MonoInst *ins = emit_simd_ins (cfg, klass, OP_XBINOP, args [0]->dreg, neg->dreg); + ins->inst_c0 = OP_IMAX; + ins->inst_c1 = arg0_type; + return ins; } #elif defined(TARGET_WASM) if (type_enum_is_float(arg0_type)) { From 3b58f1072bfe4b6d9d9f8634bc118ec8b4ff0f03 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Wed, 17 Jan 2024 11:35:53 -0800 Subject: [PATCH 081/189] Augment the backport PR description template to prompt for more info (#97073) * Augment the backport PR description template to prompt for more info * Wordsmith the backport template prompts per feedback --- .github/workflows/backport.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 3fca571086f37e..f8165363070ea5 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -24,13 +24,28 @@ jobs: ## Customer Impact + - [ ] Customer reported + - [ ] Found internally + + [Select one or both of the boxes. Describe how this issue impacts customers, citing the expected and actual behaviors and scope of the issue. If customer-reported, provide the issue number.] + + ## Regression + + - [ ] Yes + - [ ] No + + [If yes, specify when the regression was introduced. Provide the PR or commit if known.] + ## Testing + [How was the fix verified? How was the issue missed previously? What tests were added?] + ## Risk + [High/Medium/Low. Justify the indication by mentioning how risks were measured and addressed.] + **IMPORTANT**: If this backport is for a servicing release, please verify that: - The PR target branch is `release/X.0-staging`, not `release/X.0`. - If the change touches code that ships in a NuGet package, you have added the necessary [package authoring](https://github.com/dotnet/runtime/blob/main/docs/project/library-servicing.md) and gotten it explicitly reviewed. - From 5598dac557c35d264c527e22239f1b33e0489376 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 17 Jan 2024 15:02:21 -0500 Subject: [PATCH 082/189] JIT: Allow BBJ_COND false target to diverge from bbNext in layout optimization phase (#96609) Next step for #93020. Working backwards through the JIT flowgraph phases, this change allows bbFalseTarget to diverge from bbNext in Compiler::optOptimizeLayout and onwards. --- src/coreclr/jit/block.cpp | 14 +- src/coreclr/jit/block.h | 10 +- src/coreclr/jit/codegenarm.cpp | 7 + src/coreclr/jit/codegenarm64.cpp | 14 ++ src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/codegenlinear.cpp | 5 +- src/coreclr/jit/codegenloongarch64.cpp | 26 +- src/coreclr/jit/codegenriscv64.cpp | 7 + src/coreclr/jit/codegenxarch.cpp | 7 + src/coreclr/jit/compiler.h | 4 +- src/coreclr/jit/fgbasic.cpp | 96 ++++++-- src/coreclr/jit/fgdiagnostic.cpp | 9 +- src/coreclr/jit/fgopt.cpp | 326 +++++++++++-------------- src/coreclr/jit/fgprofilesynthesis.cpp | 5 +- src/coreclr/jit/jiteh.cpp | 7 - src/coreclr/jit/lower.cpp | 2 +- src/coreclr/jit/optimizer.cpp | 56 ++--- src/coreclr/jit/stacklevelsetter.cpp | 23 ++ src/coreclr/jit/switchrecognition.cpp | 30 ++- 19 files changed, 364 insertions(+), 286 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 446733c80853cd..24d37e691c9ff8 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -301,18 +301,20 @@ bool BasicBlock::CanRemoveJumpToNext(Compiler* compiler) const } //------------------------------------------------------------------------ -// CanRemoveJumpToFalseTarget: determine if jump to false target can be omitted +// CanRemoveJumpToTarget: determine if jump to target can be omitted // // Arguments: +// target - true/false target of BBJ_COND block // compiler - current compiler instance // // Returns: -// true if block is a BBJ_COND that can fall into its false target +// true if block is a BBJ_COND that can fall into target // -bool BasicBlock::CanRemoveJumpToFalseTarget(Compiler* compiler) const +bool BasicBlock::CanRemoveJumpToTarget(BasicBlock* target, Compiler* compiler) const { assert(KindIs(BBJ_COND)); - return NextIs(bbFalseTarget) && !hasAlign() && !compiler->fgInDifferentRegions(this, bbFalseTarget); + assert(TrueTargetIs(target) || FalseTargetIs(target)); + return NextIs(target) && !compiler->fgInDifferentRegions(this, target); } //------------------------------------------------------------------------ @@ -1170,7 +1172,7 @@ unsigned BasicBlock::NumSucc() const return 1; case BBJ_COND: - if (bbTarget == bbNext) + if (bbTrueTarget == bbFalseTarget) { return 1; } @@ -1295,7 +1297,7 @@ unsigned BasicBlock::NumSucc(Compiler* comp) return 1; case BBJ_COND: - if (bbTarget == bbNext) + if (bbTrueTarget == bbFalseTarget) { return 1; } diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 40405678fae98d..2eef62974eed3a 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -616,7 +616,7 @@ struct BasicBlock : private LIR::Range bool CanRemoveJumpToNext(Compiler* compiler) const; - bool CanRemoveJumpToFalseTarget(Compiler* compiler) const; + bool CanRemoveJumpToTarget(BasicBlock* target, Compiler* compiler) const; unsigned GetTargetOffs() const { @@ -669,7 +669,6 @@ struct BasicBlock : private LIR::Range { assert(KindIs(BBJ_COND)); assert(bbTrueTarget != nullptr); - assert(target != nullptr); return (bbTrueTarget == target); } @@ -696,15 +695,16 @@ struct BasicBlock : private LIR::Range { assert(KindIs(BBJ_COND)); assert(bbFalseTarget != nullptr); - assert(target != nullptr); return (bbFalseTarget == target); } void SetCond(BasicBlock* trueTarget, BasicBlock* falseTarget) { + // Switch lowering may temporarily set a block to a BBJ_COND + // with a null false target if it is the last block in the list. + // This invalid state is eventually fixed, so allow it in the below assert. + assert((falseTarget != nullptr) || (falseTarget == bbNext)); assert(trueTarget != nullptr); - // TODO-NoFallThrough: Allow falseTarget to diverge from bbNext - assert(falseTarget == bbNext); bbKind = BBJ_COND; bbTrueTarget = trueTarget; bbFalseTarget = falseTarget; diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 28908f4392d798..4a8c08a89858e8 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -1317,6 +1317,13 @@ void CodeGen::genCodeForJTrue(GenTreeOp* jtrue) regNumber reg = genConsumeReg(op); inst_RV_RV(INS_tst, reg, reg, genActualType(op)); inst_JMP(EJ_ne, compiler->compCurBB->GetTrueTarget()); + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index d9681d12012eba..489aa6a744942d 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -4650,6 +4650,13 @@ void CodeGen::genCodeForJTrue(GenTreeOp* jtrue) GenTree* op = jtrue->gtGetOp1(); regNumber reg = genConsumeReg(op); GetEmitter()->emitIns_J_R(INS_cbnz, emitActualTypeSize(op), compiler->compCurBB->GetTrueTarget(), reg); + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } } //------------------------------------------------------------------------ @@ -4877,6 +4884,13 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) GetEmitter()->emitIns_J_R(ins, attr, compiler->compCurBB->GetTrueTarget(), reg); } + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } } //--------------------------------------------------------------------- diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 09a28cc373bf49..f0471d242d812c 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -397,7 +397,7 @@ void CodeGen::genMarkLabelsForCodegen() block->GetTrueTarget()->SetFlags(BBF_HAS_LABEL); // If we need a jump to the false target, give it a label - if (!block->CanRemoveJumpToFalseTarget(compiler)) + if (!block->CanRemoveJumpToTarget(block->GetFalseTarget(), compiler)) { JITDUMP(" " FMT_BB " : branch target\n", block->GetFalseTarget()->bbNum); block->GetFalseTarget()->SetFlags(BBF_HAS_LABEL); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 83712ec0cd6694..81c84a10f45a20 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -2621,9 +2621,10 @@ void CodeGen::genCodeForJcc(GenTreeCC* jcc) inst_JCC(jcc->gtCondition, compiler->compCurBB->GetTrueTarget()); // If we cannot fall into the false target, emit a jump to it - if (!compiler->compCurBB->CanRemoveJumpToFalseTarget(compiler)) + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) { - inst_JMP(EJ_jmp, compiler->compCurBB->GetFalseTarget()); + inst_JMP(EJ_jmp, falseTarget); } } diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 1311ccc082e800..0a15bdf1dc7c89 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -4330,6 +4330,13 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) assert(regs != 0); emit->emitIns_J(ins, compiler->compCurBB->GetTrueTarget(), regs); // 5-bits; + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } } //--------------------------------------------------------------------- @@ -4874,16 +4881,31 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) case GT_JCC: { +#if !FEATURE_FIXED_OUT_ARGS BasicBlock* tgtBlock = compiler->compCurBB->KindIs(BBJ_COND) ? compiler->compCurBB->GetTrueTarget() : compiler->compCurBB->GetTarget(); -#if !FEATURE_FIXED_OUT_ARGS assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed()); #endif // !FEATURE_FIXED_OUT_ARGS GenTreeCC* jcc = treeNode->AsCC(); assert(jcc->gtCondition.Is(GenCondition::EQ, GenCondition::NE)); instruction ins = jcc->gtCondition.Is(GenCondition::EQ) ? INS_bceqz : INS_bcnez; - emit->emitIns_J(ins, tgtBlock, (int)1 /* cc */); + + if (compiler->compCurBB->KindIs(BBJ_COND)) + { + emit->emitIns_J(ins, compiler->compCurBB->GetTrueTarget(), (int)1 /* cc */); + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } + } + else + { + emit->emitIns_J(ins, compiler->compCurBB->GetTarget(), (int)1 /* cc */); + } } break; diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index d240baee699da8..f3bc1b10c5bdb0 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -3920,6 +3920,13 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) assert(regs != 0); emit->emitIns_J(ins, compiler->compCurBB->GetTrueTarget(), regs); // 5-bits; + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } } //--------------------------------------------------------------------- diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 1aac2b8890273d..1d2e0d71768b36 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -1453,6 +1453,13 @@ void CodeGen::genCodeForJTrue(GenTreeOp* jtrue) regNumber reg = genConsumeReg(op); inst_RV_RV(INS_test, reg, reg, genActualType(op)); inst_JMP(EJ_jne, compiler->compCurBB->GetTrueTarget()); + + // If we cannot fall into the false target, emit a jump to it + BasicBlock* falseTarget = compiler->compCurBB->GetFalseTarget(); + if (!compiler->compCurBB->CanRemoveJumpToTarget(falseTarget, compiler)) + { + inst_JMP(EJ_jmp, falseTarget); + } } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index c08f99c396c0b3..b0c2f05b6c03f2 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5956,7 +5956,7 @@ class Compiler void fgUpdateLoopsAfterCompacting(BasicBlock* block, BasicBlock* bNext); - BasicBlock* fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst); + BasicBlock* fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst, bool noFallThroughQuirk = false); bool fgRenumberBlocks(); @@ -6003,8 +6003,6 @@ class Compiler bool fgOptimizeSwitchBranches(BasicBlock* block); - bool fgOptimizeBranchToNext(BasicBlock* block, BasicBlock* bNext, BasicBlock* bPrev); - bool fgOptimizeSwitchJumps(); #ifdef DEBUG void fgPrintEdgeWeights(); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 1d3a3915aea302..d88ed59a8f1963 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -693,15 +693,35 @@ void Compiler::fgReplaceJumpTarget(BasicBlock* block, BasicBlock* newTarget, Bas break; case BBJ_COND: + { + FlowEdge* oldEdge = fgGetPredForBlock(oldTarget, block); + assert(oldEdge != nullptr); - // Functionally equivalent to above if (block->TrueTargetIs(oldTarget)) { - block->SetTrueTarget(newTarget); - fgRemoveRefPred(oldTarget, block); - fgAddRefPred(newTarget, block); + if (block->FalseTargetIs(oldTarget)) + { + assert(oldEdge->getDupCount() == 2); + fgRemoveConditionalJump(block); + assert(block->KindIs(BBJ_ALWAYS)); + block->SetTarget(newTarget); + } + else + { + block->SetTrueTarget(newTarget); + } + } + else + { + assert(block->FalseTargetIs(oldTarget)); + block->SetFalseTarget(newTarget); } + + assert(oldEdge->getDupCount() == 1); + fgRemoveRefPred(oldTarget, block); + fgAddRefPred(newTarget, block); break; + } case BBJ_SWITCH: { @@ -5063,13 +5083,17 @@ BasicBlock* Compiler::fgSplitEdge(BasicBlock* curr, BasicBlock* succ) if (curr->KindIs(BBJ_COND)) { - curr->SetFalseTarget(curr->Next()); - fgReplacePred(succ, curr, newBlock); if (curr->TrueTargetIs(succ)) { - // Now 'curr' jumps to newBlock curr->SetTrueTarget(newBlock); } + else + { + assert(curr->FalseTargetIs(succ)); + curr->SetFalseTarget(newBlock); + } + + fgReplacePred(succ, curr, newBlock); fgAddRefPred(newBlock, curr); } else if (curr->KindIs(BBJ_SWITCH)) @@ -5279,6 +5303,12 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) /* At this point the bbPreds and bbRefs had better be zero */ noway_assert((block->bbRefs == 0) && (block->bbPreds == nullptr)); + + if (bPrev->KindIs(BBJ_COND) && bPrev->FalseTargetIs(block)) + { + assert(bNext != nullptr); + bPrev->SetFalseTarget(bNext); + } } else // block is empty { @@ -5388,24 +5418,15 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) break; case BBJ_COND: - /* The links for the direct predecessor case have already been updated above */ - if (!predBlock->TrueTargetIs(block)) + if (predBlock->TrueTargetIs(block)) { - break; + predBlock->SetTrueTarget(succBlock); } - /* Check if both sides of the BBJ_COND now jump to the same block */ - if (predBlock->FalseTargetIs(succBlock)) + if (predBlock->FalseTargetIs(block)) { - // Make sure we are replacing "block" with "succBlock" in predBlock->bbTarget. - noway_assert(predBlock->TrueTargetIs(block)); - predBlock->SetTrueTarget(succBlock); - fgRemoveConditionalJump(predBlock); - break; + predBlock->SetFalseTarget(succBlock); } - - noway_assert(predBlock->TrueTargetIs(block)); - predBlock->SetTrueTarget(succBlock); break; case BBJ_CALLFINALLY: @@ -5440,7 +5461,9 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) break; case BBJ_COND: - bPrev->SetFalseTarget(block->Next()); + // block should not be a target anymore + assert(!bPrev->TrueTargetIs(block)); + assert(!bPrev->FalseTargetIs(block)); /* Check if both sides of the BBJ_COND now jump to the same block */ if (bPrev->TrueTargetIs(bPrev->GetFalseTarget())) @@ -5508,7 +5531,7 @@ void Compiler::fgPrepareCallFinallyRetForRemoval(BasicBlock* block) // Newly inserted block after bSrc that jumps to bDst, // or nullptr if bSrc already falls through to bDst // -BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst) +BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst, bool noFallThroughQuirk /* = false */) { assert(bSrc != nullptr); assert(fgPredsComputed); @@ -5516,8 +5539,18 @@ BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst) /* If bSrc falls through to a block that is not bDst, we will insert a jump to bDst */ - if (bSrc->KindIs(BBJ_COND) && !bSrc->NextIs(bDst)) + if (bSrc->KindIs(BBJ_COND) && bSrc->FalseTargetIs(bDst) && !bSrc->NextIs(bDst)) { + assert(fgGetPredForBlock(bDst, bSrc) != nullptr); + + // Allow the caller to decide whether to use the old logic of maintaining implicit fallthrough behavior, + // or to allow BBJ_COND blocks' false targets to diverge from bbNext. + // TODO-NoFallThrough: Remove this quirk once callers' dependencies on implicit fallthrough are gone. + if (noFallThroughQuirk) + { + return jmpBlk; + } + // Add a new block after bSrc which jumps to 'bDst' jmpBlk = fgNewBBafter(BBJ_ALWAYS, bSrc, true, bDst); bSrc->SetFalseTarget(jmpBlk); @@ -5691,8 +5724,14 @@ bool Compiler::fgRenumberBlocks() */ bool Compiler::fgIsForwardBranch(BasicBlock* bJump, BasicBlock* bDest, BasicBlock* bSrc /* = NULL */) { - assert((bJump->KindIs(BBJ_ALWAYS, BBJ_CALLFINALLYRET) && bJump->TargetIs(bDest)) || - (bJump->KindIs(BBJ_COND) && bJump->TrueTargetIs(bDest))); + if (bJump->KindIs(BBJ_COND)) + { + assert(bJump->TrueTargetIs(bDest) || bJump->FalseTargetIs(bDest)); + } + else + { + assert(bJump->KindIs(BBJ_ALWAYS, BBJ_CALLFINALLYRET) && bJump->TargetIs(bDest)); + } bool result = false; BasicBlock* bTemp = (bSrc == nullptr) ? bJump : bSrc; @@ -6627,14 +6666,17 @@ BasicBlock* Compiler::fgFindInsertPoint(unsigned regionIndex, // Look for an insert location: // 1. We want blocks that don't end with a fall through, // 2. Also, when blk equals nearBlk we may want to insert here. + // Quirk: Manually check for BBJ_COND fallthrough behavior + const bool blkFallsThrough = + blk->bbFallsThrough() && (!blk->KindIs(BBJ_COND) || blk->NextIs(blk->GetFalseTarget())); const bool blkJumpsToNext = blk->KindIs(BBJ_ALWAYS) && blk->HasFlag(BBF_NONE_QUIRK) && blk->JumpsToNext(); - if ((!blk->bbFallsThrough() && !blkJumpsToNext) || (blk == nearBlk)) + if ((!blkFallsThrough && !blkJumpsToNext) || (blk == nearBlk)) { bool updateBestBlk = true; // We will probably update the bestBlk // If blk falls through then we must decide whether to use the nearBlk // hint - if (blk->bbFallsThrough() || blkJumpsToNext) + if (blkFallsThrough || blkJumpsToNext) { noway_assert(blk == nearBlk); if (jumpBlk != nullptr) diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 95e6b6a858f6a6..2fbf905d131acf 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -136,7 +136,7 @@ void Compiler::fgDebugCheckUpdate() // A conditional branch should never jump to the next block as it can be folded into a BBJ_ALWAYS. if (block->KindIs(BBJ_COND) && block->TrueTargetIs(block->GetFalseTarget())) { - noway_assert(!"Unnecessary jump to the next block!"); + noway_assert(!"BBJ_COND true/false targets are the same!"); } // For a BBJ_CALLFINALLY block we make sure that we are followed by a BBJ_CALLFINALLYRET block @@ -2977,13 +2977,6 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef maxBBNum = max(maxBBNum, block->bbNum); - // BBJ_COND's normal (false) jump target is expected to be the next block - // TODO-NoFallThrough: Allow bbFalseTarget to diverge from bbNext - if (block->KindIs(BBJ_COND)) - { - assert(block->NextIs(block->GetFalseTarget())); - } - // Check that all the successors have the current traversal stamp. Use the 'Compiler*' version of the // iterator, but not for BBJ_SWITCH: we don't want to end up calling GetDescriptorForSwitch(), which will // dynamically create the unique switch list. diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index b48f1450a1a857..996ed5dd16f0e6 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -1516,109 +1516,6 @@ void Compiler::fgUnreachableBlock(BasicBlock* block) fgRemoveBlockAsPred(block); } -//------------------------------------------------------------- -// fgRemoveConditionalJump: Remove or morph a jump when we jump to the same -// block when both the condition is true or false. Remove the branch condition, -// but leave any required side effects. -// -// Arguments: -// block - block with conditional branch -// -void Compiler::fgRemoveConditionalJump(BasicBlock* block) -{ - noway_assert(block->KindIs(BBJ_COND) && block->TrueTargetIs(block->GetFalseTarget())); - assert(compRationalIRForm == block->IsLIR()); - - FlowEdge* flow = fgGetPredForBlock(block->GetFalseTarget(), block); - noway_assert(flow->getDupCount() == 2); - - // Change the BBJ_COND to BBJ_ALWAYS, and adjust the refCount and dupCount. - --block->GetFalseTarget()->bbRefs; - block->SetKind(BBJ_ALWAYS); - block->SetFlags(BBF_NONE_QUIRK); - flow->decrementDupCount(); - -#ifdef DEBUG - if (verbose) - { - printf("Block " FMT_BB " becoming a BBJ_ALWAYS to " FMT_BB " (jump target is the same whether the condition" - " is true or false)\n", - block->bbNum, block->GetTarget()->bbNum); - } -#endif - - // Remove the block jump condition - - if (block->IsLIR()) - { - LIR::Range& blockRange = LIR::AsRange(block); - - GenTree* test = blockRange.LastNode(); - assert(test->OperIsConditionalJump()); - - bool isClosed; - unsigned sideEffects; - LIR::ReadOnlyRange testRange = blockRange.GetTreeRange(test, &isClosed, &sideEffects); - - // TODO-LIR: this should really be checking GTF_ALL_EFFECT, but that produces unacceptable - // diffs compared to the existing backend. - if (isClosed && ((sideEffects & GTF_SIDE_EFFECT) == 0)) - { - // If the jump and its operands form a contiguous, side-effect-free range, - // remove them. - blockRange.Delete(this, block, std::move(testRange)); - } - else - { - // Otherwise, just remove the jump node itself. - blockRange.Remove(test, true); - } - } - else - { - Statement* test = block->lastStmt(); - GenTree* tree = test->GetRootNode(); - - noway_assert(tree->gtOper == GT_JTRUE); - - GenTree* sideEffList = nullptr; - - if (tree->gtFlags & GTF_SIDE_EFFECT) - { - gtExtractSideEffList(tree, &sideEffList); - - if (sideEffList) - { - noway_assert(sideEffList->gtFlags & GTF_SIDE_EFFECT); -#ifdef DEBUG - if (verbose) - { - printf("Extracted side effects list from condition...\n"); - gtDispTree(sideEffList); - printf("\n"); - } -#endif - } - } - - // Delete the cond test or replace it with the side effect tree - if (sideEffList == nullptr) - { - fgRemoveStmt(block, test); - } - else - { - test->SetRootNode(sideEffList); - - if (fgNodeThreading != NodeThreading::None) - { - gtSetStmtInfo(test); - fgSetStmtSeq(test); - } - } - } -} - //------------------------------------------------------------- // fgOptimizeBranchToEmptyUnconditional: // Optimize a jump to an empty block which ends in an unconditional branch. @@ -2684,27 +2581,25 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* } //------------------------------------------------------------- -// fgOptimizeBranchToNext: -// Optimize a block which has a branch to the following block +// fgRemoveConditionalJump: Remove or morph a jump when we jump to the same +// block when both the condition is true or false. Remove the branch condition, +// but leave any required side effects. // // Arguments: -// block - block with a BBJ_COND jump kind -// bNext - target that block jumps to regardless of its condition -// bPrev - block which is prior to the first block -// -// Returns: true if changes were made +// block - block with conditional branch // -bool Compiler::fgOptimizeBranchToNext(BasicBlock* block, BasicBlock* bNext, BasicBlock* bPrev) +void Compiler::fgRemoveConditionalJump(BasicBlock* block) { assert(block->KindIs(BBJ_COND)); - assert(block->TrueTargetIs(bNext)); - assert(block->FalseTargetIs(bNext)); - assert(block->PrevIs(bPrev)); + assert(block->FalseTargetIs(block->GetTrueTarget())); + BasicBlock* target = block->GetTrueTarget(); #ifdef DEBUG if (verbose) { - printf("\nRemoving conditional jump to next block (" FMT_BB " -> " FMT_BB ")\n", block->bbNum, bNext->bbNum); + printf("Block " FMT_BB " becoming a BBJ_ALWAYS to " FMT_BB " (jump target is the same whether the condition" + " is true or false)\n", + block->bbNum, target->bbNum); } #endif // DEBUG @@ -2803,17 +2698,20 @@ bool Compiler::fgOptimizeBranchToNext(BasicBlock* block, BasicBlock* bNext, Basi } } - /* Conditional is gone - always jump to the next block */ + /* Conditional is gone - always jump to target */ block->SetKind(BBJ_ALWAYS); + if (block->JumpsToNext()) + { + block->SetFlags(BBF_NONE_QUIRK); + } + /* Update bbRefs and bbNum - Conditional predecessors to the same * block are counted twice so we have to remove one of them */ - noway_assert(bNext->countOfInEdges() > 1); - fgRemoveRefPred(bNext, block); - - return true; + noway_assert(target->countOfInEdges() > 1); + fgRemoveRefPred(target, block); } //------------------------------------------------------------- @@ -3736,11 +3634,13 @@ bool Compiler::fgReorderBlocks(bool useProfile) continue; } - bool reorderBlock = useProfile; - const bool isRare = block->isRunRarely(); - BasicBlock* bDest = nullptr; - bool forwardBranch = false; - bool backwardBranch = false; + bool reorderBlock = useProfile; + const bool isRare = block->isRunRarely(); + BasicBlock* bDest = nullptr; + BasicBlock* bFalseDest = nullptr; + bool forwardBranch = false; + bool backwardBranch = false; + bool forwardFalseBranch = false; // Setup bDest if (bPrev->KindIs(BBJ_ALWAYS, BBJ_CALLFINALLYRET)) @@ -3751,9 +3651,13 @@ bool Compiler::fgReorderBlocks(bool useProfile) } else if (bPrev->KindIs(BBJ_COND)) { - bDest = bPrev->GetTrueTarget(); - forwardBranch = fgIsForwardBranch(bPrev, bDest); - backwardBranch = !forwardBranch; + assert(bPrev->FalseTargetIs(block) || useProfile); + bDest = bPrev->GetTrueTarget(); + bFalseDest = bPrev->GetFalseTarget(); + // TODO: Cheaper to call fgRenumberBlocks first and compare bbNums? + forwardBranch = fgIsForwardBranch(bPrev, bDest); + backwardBranch = !forwardBranch; + forwardFalseBranch = fgIsForwardBranch(bPrev, bFalseDest); } // We will look for bPrev as a non rarely run block followed by block as a rarely run block @@ -3886,43 +3790,55 @@ bool Compiler::fgReorderBlocks(bool useProfile) // We will check that the min weight of the bPrev to bDest edge // is more than twice the max weight of the bPrev to block edge. // - // bPrev --> [BB04, weight 31] + // bPrev --------> [BB04, weight 31] // | \. - // edgeToBlock -------------> O \. + // falseEdge ---------------> O \. // [min=8,max=10] V \. - // block --> [BB05, weight 10] \. + // bFalseDest ---> [BB05, weight 10] \. // \. - // edgeToDest ----------------------------> O + // trueEdge ------------------------------> O // [min=21,max=23] | // V // bDest ---------------> [BB08, weight 21] // - FlowEdge* edgeToDest = fgGetPredForBlock(bDest, bPrev); - FlowEdge* edgeToBlock = fgGetPredForBlock(block, bPrev); - noway_assert(edgeToDest != nullptr); - noway_assert(edgeToBlock != nullptr); + FlowEdge* trueEdge = fgGetPredForBlock(bDest, bPrev); + FlowEdge* falseEdge = fgGetPredForBlock(bFalseDest, bPrev); + noway_assert(trueEdge != nullptr); + noway_assert(falseEdge != nullptr); // // Calculate the taken ratio - // A takenRation of 0.10 means taken 10% of the time, not taken 90% of the time - // A takenRation of 0.50 means taken 50% of the time, not taken 50% of the time - // A takenRation of 0.90 means taken 90% of the time, not taken 10% of the time + // A takenRatio of 0.10 means taken 10% of the time, not taken 90% of the time + // A takenRatio of 0.50 means taken 50% of the time, not taken 50% of the time + // A takenRatio of 0.90 means taken 90% of the time, not taken 10% of the time // double takenCount = - ((double)edgeToDest->edgeWeightMin() + (double)edgeToDest->edgeWeightMax()) / 2.0; + ((double)trueEdge->edgeWeightMin() + (double)trueEdge->edgeWeightMax()) / 2.0; double notTakenCount = - ((double)edgeToBlock->edgeWeightMin() + (double)edgeToBlock->edgeWeightMax()) / 2.0; + ((double)falseEdge->edgeWeightMin() + (double)falseEdge->edgeWeightMax()) / 2.0; double totalCount = takenCount + notTakenCount; // If the takenRatio (takenCount / totalCount) is greater or equal to 51% then we will reverse // the branch if (takenCount < (0.51 * totalCount)) { - reorderBlock = false; + // We take bFalseDest more often. + // If bFalseDest is a backward branch, we should reverse the condition. + // Else if both branches are forward, not much use in reversing the condition. + reorderBlock = !forwardFalseBranch; + } + else if (bFalseDest == block) + { + // We take bDest more often, and we can fall into bFalseDest, + // so it makes sense to reverse the condition. + profHotWeight = (falseEdge->edgeWeightMin() + falseEdge->edgeWeightMax()) / 2 - 1; } else { - // set profHotWeight - profHotWeight = (edgeToBlock->edgeWeightMin() + edgeToBlock->edgeWeightMax()) / 2 - 1; + // We take bDest more often than bFalseDest, but bFalseDest isn't the next block, + // so reversing the branch doesn't make sense since bDest is already a forward branch. + assert(takenCount >= (0.51 * totalCount)); + assert(bFalseDest != block); + reorderBlock = false; } } else @@ -3956,8 +3872,16 @@ bool Compiler::fgReorderBlocks(bool useProfile) profHotWeight = (weightDest < weightPrev) ? weightDest : weightPrev; // if the weight of block is greater (or equal) to profHotWeight then we don't reverse the cond - if (block->bbWeight >= profHotWeight) + if (bDest->bbWeight >= profHotWeight) + { + // bFalseDest has a greater weight, so if it is a backward branch, + // we should reverse the branch. + reorderBlock = !forwardFalseBranch; + } + else if (bFalseDest != block) { + // bFalseDest is taken less often, but it isn't the next block, + // so it doesn't make sense to reverse the branch. reorderBlock = false; } } @@ -4019,7 +3943,9 @@ bool Compiler::fgReorderBlocks(bool useProfile) } const bool bTmpJumpsToNext = bTmp->KindIs(BBJ_ALWAYS, BBJ_CALLFINALLYRET) && bTmp->JumpsToNext(); - if ((!bTmp->bbFallsThrough() && !bTmpJumpsToNext) || (bTmp->bbWeight == BB_ZERO_WEIGHT)) + const bool bTmpFallsThrough = + bTmp->bbFallsThrough() && (!bTmp->KindIs(BBJ_COND) || bTmp->NextIs(bTmp->GetFalseTarget())); + if ((!bTmpJumpsToNext && !bTmpFallsThrough) || (bTmp->bbWeight == BB_ZERO_WEIGHT)) { lastNonFallThroughBlock = bTmp; } @@ -4211,7 +4137,7 @@ bool Compiler::fgReorderBlocks(bool useProfile) } // Set connected_bDest to true if moving blocks [bStart .. bEnd] - // connects with the jump dest of bPrev (i.e bDest) and + // connects with the jump dest of bPrev (i.e bDest) and // thus allows bPrev fall through instead of jump. if (bNext == bDest) { @@ -4290,7 +4216,12 @@ bool Compiler::fgReorderBlocks(bool useProfile) { // Treat jumps to next block as fall-through } - else if (bEnd2->bbFallsThrough() == false) + else if (bEnd2->KindIs(BBJ_COND) && !bEnd2->FalseTargetIs(bNext)) + { + // Block does not fall through into false target + break; + } + else if (!bEnd2->bbFallsThrough()) { break; } @@ -4378,7 +4309,7 @@ bool Compiler::fgReorderBlocks(bool useProfile) } else { - if (bPrev->bbFallsThrough()) + if (bPrev->KindIs(BBJ_COND) && bPrev->NextIs(bPrev->GetFalseTarget())) { printf("since it falls into a rarely run block\n"); } @@ -4474,6 +4405,8 @@ bool Compiler::fgReorderBlocks(bool useProfile) // Find new location for the unlinked block(s) // Set insertAfterBlk to the block which will precede the insertion point + EHblkDsc* ehDsc; + if (!bStart->hasTryIndex() && isRare) { // We'll just insert the blocks at the end of the method. If the method @@ -4488,7 +4421,7 @@ bool Compiler::fgReorderBlocks(bool useProfile) { BasicBlock* startBlk; BasicBlock* lastBlk; - EHblkDsc* ehDsc = ehInitTryBlockRange(bStart, &startBlk, &lastBlk); + ehDsc = ehInitTryBlockRange(bStart, &startBlk, &lastBlk); BasicBlock* endBlk; @@ -4730,6 +4663,11 @@ bool Compiler::fgReorderBlocks(bool useProfile) noway_assert(condTest->gtOper == GT_JTRUE); condTest->AsOp()->gtOp1 = gtReverseCond(condTest->AsOp()->gtOp1); + BasicBlock* trueTarget = bPrev->GetTrueTarget(); + BasicBlock* falseTarget = bPrev->GetFalseTarget(); + bPrev->SetTrueTarget(falseTarget); + bPrev->SetFalseTarget(trueTarget); + // may need to rethread // if (fgNodeThreading == NodeThreading::AllTrees) @@ -4739,18 +4677,10 @@ bool Compiler::fgReorderBlocks(bool useProfile) fgSetStmtSeq(condTestStmt); } - if (bStart2 == nullptr) - { - /* Set the new jump dest for bPrev to the rarely run or uncommon block(s) */ - bPrev->SetTrueTarget(bStart); - } - else + if (bStart2 != nullptr) { noway_assert(insertAfterBlk == bPrev); noway_assert(insertAfterBlk->NextIs(block)); - - /* Set the new jump dest for bPrev to the rarely run or uncommon block(s) */ - bPrev->SetTrueTarget(block); } } @@ -4789,32 +4719,35 @@ bool Compiler::fgReorderBlocks(bool useProfile) /* We have decided to insert the block(s) after 'insertAfterBlk' */ fgMoveBlocksAfter(bStart, bEnd, insertAfterBlk); + // useProfile should be true only when finalizing the block layout in Compiler::optOptimizeLayout. + // In this final pass, allow BBJ_COND blocks' false targets to diverge from bbNext. + // TODO-NoFallThrough: Always allow the false targets to diverge. if (bDest) { /* We may need to insert an unconditional branch after bPrev to bDest */ - fgConnectFallThrough(bPrev, bDest); + fgConnectFallThrough(bPrev, bDest, /* noFallThroughQuirk */ useProfile); } else { /* If bPrev falls through, we must insert a jump to block */ - fgConnectFallThrough(bPrev, block); + fgConnectFallThrough(bPrev, block, /* noFallThroughQuirk */ useProfile); } BasicBlock* bSkip = bEnd->Next(); /* If bEnd falls through, we must insert a jump to bNext */ - fgConnectFallThrough(bEnd, bNext); + fgConnectFallThrough(bEnd, bNext, /* noFallThroughQuirk */ useProfile); if (bStart2 == nullptr) { /* If insertAfterBlk falls through, we are forced to */ /* add a jump around the block(s) we just inserted */ - fgConnectFallThrough(insertAfterBlk, bSkip); + fgConnectFallThrough(insertAfterBlk, bSkip, /* noFallThroughQuirk */ useProfile); } else { /* We may need to insert an unconditional branch after bPrev2 to bStart */ - fgConnectFallThrough(bPrev2, bStart); + fgConnectFallThrough(bPrev2, bStart, /* noFallThroughQuirk */ useProfile); } #if DEBUG @@ -5001,25 +4934,41 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } else if (block->KindIs(BBJ_COND)) { - bDest = block->GetTrueTarget(); - if (bDest == bNext) + bDest = block->GetTrueTarget(); + BasicBlock* bFalseDest = block->GetFalseTarget(); + assert(bFalseDest != nullptr); + + if (bFalseDest->KindIs(BBJ_ALWAYS) && bFalseDest->TargetIs(bDest) && bFalseDest->isEmpty()) { - // TODO-NoFallThrough: Fix above condition once bbFalseTarget can diverge from bbNext - assert(block->FalseTargetIs(bNext)); - if (fgOptimizeBranchToNext(block, bNext, bPrev)) - { - change = true; - modified = true; - bDest = nullptr; - } + // Optimize bFalseDest -> BBJ_ALWAYS -> bDest + block->SetFalseTarget(bDest); + fgRemoveRefPred(bFalseDest, block); + fgAddRefPred(bDest, block); + } + else if (bDest->KindIs(BBJ_ALWAYS) && bDest->TargetIs(bFalseDest) && bDest->isEmpty()) + { + // Optimize bDest -> BBJ_ALWAYS -> bFalseDest + block->SetTrueTarget(bFalseDest); + fgRemoveRefPred(bDest, block); + fgAddRefPred(bFalseDest, block); + bDest = bFalseDest; + } + + if (block->FalseTargetIs(bDest)) + { + fgRemoveConditionalJump(block); + change = true; + modified = true; + assert(block->KindIs(BBJ_ALWAYS)); + assert(block->TargetIs(bDest)); } } if (bDest != nullptr) { // Do we have a JUMP to an empty unconditional JUMP block? - if (bDest->isEmpty() && bDest->KindIs(BBJ_ALWAYS) && - !bDest->TargetIs(bDest)) // special case for self jumps + if (bDest->KindIs(BBJ_ALWAYS) && !bDest->TargetIs(bDest) && // special case for self jumps + bDest->isEmpty()) { // TODO: Allow optimizing branches to blocks that jump to the next block const bool optimizeBranch = !bDest->JumpsToNext() || !bDest->HasFlag(BBF_NONE_QUIRK); @@ -5039,18 +4988,18 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) // (b) block jump target is elsewhere but join free, and // bNext's jump target has a join. // - if (block->KindIs(BBJ_COND) && // block is a BBJ_COND block - (bNext->bbRefs == 1) && // No other block jumps to bNext - bNext->KindIs(BBJ_ALWAYS) && // The next block is a BBJ_ALWAYS block - !bNext->JumpsToNext() && // and it doesn't jump to the next block (we might compact them) - bNext->isEmpty() && // and it is an empty block - !bNext->TargetIs(bNext) && // special case for self jumps + if (block->KindIs(BBJ_COND) && // block is a BBJ_COND block + (bNext != nullptr) && // block isn't the last block + block->FalseTargetIs(bNext) && // block falls into its false target + (bNext->bbRefs == 1) && // No other block jumps to bNext + bNext->KindIs(BBJ_ALWAYS) && // The next block is a BBJ_ALWAYS block + !bNext->JumpsToNext() && // and it doesn't jump to the next block (we might compact them) + bNext->isEmpty() && // and it is an empty block + !bNext->TargetIs(bNext) && // special case for self jumps !bDest->IsFirstColdBlock(this) && !fgInDifferentRegions(block, bDest)) // do not cross hot/cold sections { - // bbFalseTarget cannot be null - assert(block->FalseTargetIs(bNext)); - assert(bNext != nullptr); + assert(!block->IsLast()); // case (a) // @@ -5106,6 +5055,12 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } } + // TODO-NoFallThrough: Remove this requirement + if (bDest->KindIs(BBJ_COND) && !bDest->NextIs(bDest->GetFalseTarget())) + { + optimizeJump = false; + } + if (optimizeJump && isJumpToJoinFree) { // In the join free case, we also need to move bDest right after bNext @@ -5186,6 +5141,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } // Optimize the Conditional JUMP to go to the new target + assert(block->FalseTargetIs(bNext)); block->SetTrueTarget(bNext->GetTarget()); block->SetFalseTarget(bNext->Next()); diff --git a/src/coreclr/jit/fgprofilesynthesis.cpp b/src/coreclr/jit/fgprofilesynthesis.cpp index 923d3276664988..1bf41de2dab921 100644 --- a/src/coreclr/jit/fgprofilesynthesis.cpp +++ b/src/coreclr/jit/fgprofilesynthesis.cpp @@ -335,9 +335,12 @@ void ProfileSynthesis::AssignLikelihoodSwitch(BasicBlock* block) { // Assume each switch case is equally probable // + const unsigned n = block->NumSucc(); assert(n != 0); - const weight_t p = 1 / (weight_t)n; + + // Duplicate zero check to silence "divide by zero" compiler warning + const weight_t p = (n != 0) ? (1 / (weight_t)n) : 0; // Each unique edge gets some multiple of that basic probability // diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index f1bdeeb22265fd..7242380cb605b7 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -2256,13 +2256,6 @@ bool Compiler::fgNormalizeEHCase2() // fgReplaceJumpTarget(predBlock, newTryStart, insertBeforeBlk); - if (predBlock->NextIs(newTryStart) && predBlock->KindIs(BBJ_COND)) - { - predBlock->SetFalseTarget(newTryStart); - fgRemoveRefPred(insertBeforeBlk, predBlock); - fgAddRefPred(newTryStart, predBlock); - } - JITDUMP("Redirect " FMT_BB " target from " FMT_BB " to " FMT_BB ".\n", predBlock->bbNum, insertBeforeBlk->bbNum, newTryStart->bbNum); } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 24008029fec9db..dd5fc6b3673bac 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -7200,7 +7200,7 @@ PhaseStatus Lowering::DoPhase() // local var liveness can delete code, which may create empty blocks if (comp->opts.OptimizationEnabled()) { - bool modified = comp->fgUpdateFlowGraph(); + bool modified = comp->fgUpdateFlowGraph(false, false); modified |= comp->fgRemoveDeadBlocks(); if (modified) diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 30b3fb845b6344..e26af00a4a5048 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -4648,7 +4648,7 @@ PhaseStatus Compiler::optOptimizeLayout() { noway_assert(opts.OptimizationEnabled()); - fgUpdateFlowGraph(/* doTailDuplication */ false); + fgUpdateFlowGraph(); fgReorderBlocks(/* useProfile */ true); fgUpdateFlowGraph(); @@ -5034,47 +5034,29 @@ bool Compiler::optCreatePreheader(FlowGraphNaturalLoop* loop) header->bbNum, enterBlock->bbNum, preheader->bbNum); fgReplaceJumpTarget(enterBlock, preheader, header); - - // Fix up fall through - if (enterBlock->KindIs(BBJ_COND) && enterBlock->NextIs(header)) - { - FlowEdge* edge = fgRemoveRefPred(header, enterBlock); - BasicBlock* newAlways = fgNewBBafter(BBJ_ALWAYS, enterBlock, true, preheader); - fgAddRefPred(preheader, newAlways, edge); - fgAddRefPred(newAlways, enterBlock, edge); - - JITDUMP("Created new BBJ_ALWAYS " FMT_BB " to redirect " FMT_BB " to preheader\n", newAlways->bbNum, - enterBlock->bbNum); - enterBlock->SetFalseTarget(newAlways); - } } - // Fix up potential fallthrough into the preheader. - BasicBlock* fallthroughSource = preheader->Prev(); - if ((fallthroughSource != nullptr) && fallthroughSource->KindIs(BBJ_COND)) + // For BBJ_COND blocks preceding header, fgReplaceJumpTarget set their false targets to preheader. + // Direct fallthrough to preheader by inserting a jump after the block. + // TODO-NoFallThrough: Remove this, and just rely on fgReplaceJumpTarget to do the fixup + BasicBlock* fallthroughSource = header->Prev(); + if (fallthroughSource->KindIs(BBJ_COND) && fallthroughSource->FalseTargetIs(preheader)) { - if (!loop->ContainsBlock(fallthroughSource)) - { - // Either unreachable or an enter edge. The new fallthrough into - // the preheader is what we want. We still need to update refs - // which fgReplaceJumpTarget doesn't do for fallthrough. - FlowEdge* old = fgRemoveRefPred(header, fallthroughSource); - fgAddRefPred(preheader, fallthroughSource, old); - } - else - { - // For a backedge we need to make sure we're still going to the head, - // and not falling into the preheader. - FlowEdge* edge = fgRemoveRefPred(header, fallthroughSource); - BasicBlock* newAlways = fgNewBBafter(BBJ_ALWAYS, fallthroughSource, true, header); - fgAddRefPred(header, newAlways, edge); - fgAddRefPred(newAlways, fallthroughSource, edge); + FlowEdge* edge = fgRemoveRefPred(preheader, fallthroughSource); + BasicBlock* newAlways = fgNewBBafter(BBJ_ALWAYS, fallthroughSource, true, preheader); + fgAddRefPred(preheader, newAlways, edge); + fgAddRefPred(newAlways, fallthroughSource, edge); - JITDUMP("Created new BBJ_ALWAYS " FMT_BB " to redirect " FMT_BB " over preheader\n", newAlways->bbNum, - fallthroughSource->bbNum); - } + JITDUMP("Created new BBJ_ALWAYS " FMT_BB " to redirect " FMT_BB " to preheader\n", newAlways->bbNum, + fallthroughSource->bbNum); + fallthroughSource->SetFalseTarget(newAlways); + } - fallthroughSource->SetFalseTarget(fallthroughSource->Next()); + // Make sure false target is set correctly for fallthrough into preheader. + if (!preheader->IsFirst()) + { + fallthroughSource = preheader->Prev(); + assert(!fallthroughSource->KindIs(BBJ_COND) || fallthroughSource->FalseTargetIs(preheader)); } optSetPreheaderWeight(loop, preheader); diff --git a/src/coreclr/jit/stacklevelsetter.cpp b/src/coreclr/jit/stacklevelsetter.cpp index b6aabca83e32d4..4f37e1621e6fd5 100644 --- a/src/coreclr/jit/stacklevelsetter.cpp +++ b/src/coreclr/jit/stacklevelsetter.cpp @@ -81,6 +81,29 @@ PhaseStatus StackLevelSetter::DoPhase() } } + // The above loop might have moved a BBJ_COND's true target to its next block. + // In such cases, reverse the condition so we can remove a branch. + if (madeChanges) + { + for (BasicBlock* const block : comp->Blocks()) + { + if (block->KindIs(BBJ_COND) && block->CanRemoveJumpToTarget(block->GetTrueTarget(), comp)) + { + // Reverse the jump condition + GenTree* test = block->lastNode(); + assert(test->OperIsConditionalJump()); + GenTree* cond = comp->gtReverseCond(test); + assert(cond == test); // Ensure `gtReverseCond` did not create a new node. + + BasicBlock* newFalseTarget = block->GetTrueTarget(); + BasicBlock* newTrueTarget = block->GetFalseTarget(); + block->SetTrueTarget(newTrueTarget); + block->SetFalseTarget(newFalseTarget); + assert(block->CanRemoveJumpToTarget(newFalseTarget, comp)); + } + } + } + return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } diff --git a/src/coreclr/jit/switchrecognition.cpp b/src/coreclr/jit/switchrecognition.cpp index 7ab33d07c3c34b..fa6abd0f23e8bb 100644 --- a/src/coreclr/jit/switchrecognition.cpp +++ b/src/coreclr/jit/switchrecognition.cpp @@ -349,6 +349,31 @@ bool Compiler::optSwitchConvert(BasicBlock* firstBlock, int testsCount, ssize_t* assert((jumpCount > 0) && (jumpCount <= SWITCH_MAX_DISTANCE + 1)); const auto jmpTab = new (this, CMK_BasicBlock) BasicBlock*[jumpCount + 1 /*default case*/]; + // Quirk: lastBlock's false target may have diverged from bbNext. If the false target is behind firstBlock, + // we may create a cycle in the BasicBlock list by setting firstBlock->bbNext to it. + // Add a new BBJ_ALWAYS to the false target to avoid this. + // (We only need this if the false target is behind firstBlock, + // but it's cheaper to just check if the false target has diverged) + // TODO-NoFallThrough: Revisit this quirk? + bool skipPredRemoval = false; + if (!lastBlock->FalseTargetIs(lastBlock->Next())) + { + if (isReversed) + { + assert(lastBlock->FalseTargetIs(blockIfTrue)); + fgRemoveRefPred(blockIfTrue, firstBlock); + blockIfTrue = fgNewBBafter(BBJ_ALWAYS, firstBlock, true, blockIfTrue); + fgAddRefPred(blockIfTrue->GetTarget(), blockIfTrue); + skipPredRemoval = true; + } + else + { + assert(lastBlock->FalseTargetIs(blockIfFalse)); + blockIfFalse = fgNewBBafter(BBJ_ALWAYS, firstBlock, true, blockIfFalse); + fgAddRefPred(blockIfFalse->GetTarget(), blockIfFalse); + } + } + fgHasSwitch = true; firstBlock->GetSwitchTargets()->bbsCount = jumpCount + 1; firstBlock->GetSwitchTargets()->bbsHasDefault = true; @@ -368,7 +393,10 @@ bool Compiler::optSwitchConvert(BasicBlock* firstBlock, int testsCount, ssize_t* } // Unlink blockIfTrue from firstBlock, we're going to link it again in the loop below. - fgRemoveRefPred(blockIfTrue, firstBlock); + if (!skipPredRemoval) + { + fgRemoveRefPred(blockIfTrue, firstBlock); + } for (unsigned i = 0; i < jumpCount; i++) { From 7989f18c482ad98db2a19218d0014b63052b144e Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 17 Jan 2024 12:31:31 -0800 Subject: [PATCH 083/189] [NativeAOT] Inline TLS access for windows/x64 (#89472) * wip * working model * wip * wip * working * Add helper for tlsIndex * add methods in superpmi * revert some local changes * misc fixes * Stop emitting TLS access code for windows/x64 * fix linux build errors * Do not throw not implemented for windows/x64 * fix the problem where ThreadStaticBase helper was still getting invoked * Revert certain changes from JIT method * Introduce getThreadLocalStaticInfo_ReadyToRun() * Consume getThreadLocalStaticInfo_ReadyToRun() * Remove getTlsRootInfo() and other methods * Revert unneeded changes * missing gtInitCldHnd initialization * save target address * jit format * run thunkgenerator * resolve merge conflicts * fix issues so the TLS is inlined * Rename data structures from *_ReadyToRun to *_NativeAOT * jit format * fix some unit test * fix a bug * fix the weird jump problem * use enclosing type cls handle for VN of static gc/non-gc helper * fix a bug of resetting the flag * useEnclosingTypeOnly from runtime to determine if VN should optimize it * do not use vnf, but only use useEnclosingTypeAsArg0 * Use GT_COMMA to add GCStaticBase call next to TLS call * optimize the cctor call * Remove lazy ctor generation from tls * Update jitinterface to not fetch data for lazy ctor * fix errors after merge * fix test build errors * fix bug in CSE * Use CORINFO_FLG_FIELD_INITCLASS instead of separate flag * Use the INITCLASS flag * Remove useEnclosingTypeOnly * Add NoCtor * Use CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR * Minor cleanup * Renegenrate thunk * Add the SetFalseTarget * fix merge conflict resolution * better handling of GTF_ICON_SECREL_OFFSET better handling of GTF_ICON_SECREL_OFFSET * review feedback * Disable optimization for minopts * Add comments around iiaSecRel * jit format * create emitNewInstrCns() * Expand TLS even if optimization is disabled * Track t_inlinedThreadStaticBase Better tracking `t_inlinedThreadStaticBase` as TYP_REF * jit format --- src/coreclr/inc/corinfo.h | 17 ++ src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/inc/jithelpers.h | 1 + src/coreclr/jit/ICorJitInfo_names_generated.h | 1 + .../jit/ICorJitInfo_wrapper_generated.hpp | 8 + src/coreclr/jit/codegenxarch.cpp | 19 +- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/compiler.hpp | 1 + src/coreclr/jit/emit.h | 4 + src/coreclr/jit/emitxarch.cpp | 21 +- src/coreclr/jit/gentree.cpp | 5 + src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/helperexpansion.cpp | 235 +++++++++++++++++- src/coreclr/jit/importer.cpp | 34 ++- src/coreclr/jit/instr.h | 2 + src/coreclr/jit/utils.cpp | 1 + src/coreclr/jit/valuenum.cpp | 4 + src/coreclr/jit/valuenumfuncs.h | 1 + .../Common/JitInterface/CorInfoHelpFunc.cs | 5 +- .../JitInterface/CorInfoImpl_generated.cs | 167 +++++++------ .../tools/Common/JitInterface/CorInfoTypes.cs | 11 +- .../ThunkGenerator/ThunkInput.txt | 2 + .../DependencyAnalysis/NodeFactory.cs | 2 +- .../Target_X64/X64ReadyToRunHelperNode.cs | 42 +--- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 7 + .../JitInterface/CorInfoImpl.RyuJit.cs | 24 ++ .../aot/jitinterface/jitinterface_generated.h | 9 + .../tools/superpmi/superpmi-shared/agnostic.h | 15 ++ .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 42 ++++ .../superpmi/superpmi-shared/methodcontext.h | 15 +- .../superpmi-shim-collector/icorjitinfo.cpp | 7 + .../icorjitinfo_generated.cpp | 7 + .../icorjitinfo_generated.cpp | 6 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 + src/coreclr/vm/jitinterface.cpp | 7 + 37 files changed, 588 insertions(+), 156 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 45b520935b0435..e957885418ca7b 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -596,6 +596,7 @@ enum CorInfoHelpFunc CORINFO_HELP_READYTORUN_GCSTATIC_BASE, // static gc field access CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE, // static non gc field access CORINFO_HELP_READYTORUN_THREADSTATIC_BASE, + CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR, CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE, CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, CORINFO_HELP_READYTORUN_GENERIC_HANDLE, @@ -1739,6 +1740,17 @@ struct CORINFO_THREAD_STATIC_BLOCKS_INFO uint32_t offsetOfGCDataPointer; }; +//---------------------------------------------------------------------------- +// getThreadLocalStaticInfo_NativeAOT and CORINFO_THREAD_STATIC_INFO_NATIVEAOT: The EE instructs the JIT about how to access a thread local field + +struct CORINFO_THREAD_STATIC_INFO_NATIVEAOT +{ + uint32_t offsetOfThreadLocalStoragePointer; + CORINFO_CONST_LOOKUP tlsRootObject; + CORINFO_CONST_LOOKUP tlsIndexObject; + CORINFO_CONST_LOOKUP threadStaticBaseSlow; +}; + //---------------------------------------------------------------------------- // Exception handling @@ -2832,6 +2844,10 @@ class ICorStaticInfo bool isGCType ) = 0; + virtual void getThreadLocalStaticInfo_NativeAOT( + CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo + ) = 0; + // Returns true iff "fldHnd" represents a static field. virtual bool isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) = 0; @@ -3366,6 +3382,7 @@ class ICorDynamicInfo : public ICorStaticInfo // It would be nicer to use existing IMAGE_REL_XXX constants instead of defining our own here... #define IMAGE_REL_BASED_REL32 0x10 #define IMAGE_REL_BASED_THUMB_BRANCH24 0x13 +#define IMAGE_REL_SECREL 0x104 // The identifier for ARM32-specific PC-relative address // computation corresponds to the following instruction diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 3ea40bbc29b16c..af9e1b9662ba50 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -402,6 +402,9 @@ void getThreadLocalStaticBlocksInfo( CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) override; +void getThreadLocalStaticInfo_NativeAOT( + CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) override; + bool isFieldStatic( CORINFO_FIELD_HANDLE fldHnd) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 5f4c584fc93a5d..6262bcdf852760 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 0cb8113f-52e5-444f-b713-dcb749b5d211 */ - 0x0cb8113f, - 0x52e5, - 0x444f, - {0xb7, 0x13, 0xdc, 0xb7, 0x49, 0xb5, 0xd2, 0x11} +constexpr GUID JITEEVersionIdentifier = { /* d7bbeb5a-aa7d-43ec-b29e-6f24dd3bca9c */ + 0xd7bbeb5a, + 0xaa7d, + 0x43ec, + {0xb2, 0x9e, 0x6f, 0x24, 0xdd, 0x3b, 0xca, 0x9c} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/inc/jithelpers.h b/src/coreclr/inc/jithelpers.h index 82253956949b5b..65167abd6a4dd6 100644 --- a/src/coreclr/inc/jithelpers.h +++ b/src/coreclr/inc/jithelpers.h @@ -264,6 +264,7 @@ JITHELPER(CORINFO_HELP_READYTORUN_GCSTATIC_BASE, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) + JITHELPER(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE, NULL,CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) JITHELPER(CORINFO_HELP_READYTORUN_GENERIC_HANDLE, NULL, CORINFO_HELP_SIG_NO_ALIGN_STUB) diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 51779cdf94bfe3..6e2e675b293db0 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -100,6 +100,7 @@ DEF_CLR_API(getFieldOffset) DEF_CLR_API(getFieldInfo) DEF_CLR_API(getThreadLocalFieldInfo) DEF_CLR_API(getThreadLocalStaticBlocksInfo) +DEF_CLR_API(getThreadLocalStaticInfo_NativeAOT) DEF_CLR_API(isFieldStatic) DEF_CLR_API(getArrayOrStringLength) DEF_CLR_API(getBoundaries) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index c90ae13375c8f6..a0ddadf7a8221a 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -950,6 +950,14 @@ void WrapICorJitInfo::getThreadLocalStaticBlocksInfo( API_LEAVE(getThreadLocalStaticBlocksInfo); } +void WrapICorJitInfo::getThreadLocalStaticInfo_NativeAOT( + CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + API_ENTER(getThreadLocalStaticInfo_NativeAOT); + wrapHnd->getThreadLocalStaticInfo_NativeAOT(pInfo); + API_LEAVE(getThreadLocalStaticInfo_NativeAOT); +} + bool WrapICorJitInfo::isFieldStatic( CORINFO_FIELD_HANDLE fldHnd) { diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 1d2e0d71768b36..e44c4247fe5521 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -411,9 +411,17 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, // instruction selection due to different memory placement at runtime. if (EA_IS_RELOC(origAttr) && genDataIndirAddrCanBeEncodedAsPCRelOffset(imm)) { - // We will use lea so displacement and not immediate will be relocatable - size = EA_SET_FLG(EA_REMOVE_FLG(size, EA_CNS_RELOC_FLG), EA_DSP_RELOC_FLG); - GetEmitter()->emitIns_R_AI(INS_lea, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + if (!EA_IS_CNS_SEC_RELOC(origAttr)) + { + // We will use lea so displacement and not immediate will be relocatable + size = EA_SET_FLG(EA_REMOVE_FLG(size, EA_CNS_RELOC_FLG), EA_DSP_RELOC_FLG); + GetEmitter()->emitIns_R_AI(INS_lea, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + } + else + { + // For section constant, the immediate will be relocatable + GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + } } else { @@ -616,6 +624,11 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre attr = EA_SET_FLG(attr, EA_BYREF_FLG); } + if (con->IsIconHandle(GTF_ICON_SECREL_OFFSET)) + { + attr = EA_SET_FLG(attr, EA_CNS_SEC_RELOC); + } + instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal, INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags)); regSet.verifyRegUsed(targetReg); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b0c2f05b6c03f2..90dd74a9a92edf 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5807,6 +5807,7 @@ class Compiler PhaseStatus fgExpandThreadLocalAccess(); bool fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call); + bool fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call); PhaseStatus fgExpandStaticInit(); bool fgExpandStaticInitForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call); diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index c7554872f7e433..4f10b648efb549 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3941,6 +3941,7 @@ inline bool Compiler::IsSharedStaticHelper(GenTree* tree) #ifdef FEATURE_READYTORUN helper == CORINFO_HELP_READYTORUN_GENERIC_STATIC_BASE || helper == CORINFO_HELP_READYTORUN_GCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE || helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE || + helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR || helper == CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE || #endif helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS; diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index f896e5258741b4..51ed6b72a0c520 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1021,6 +1021,7 @@ class emitter { return iiaJmpOffset; } + #elif defined(TARGET_RISCV64) struct { @@ -1039,6 +1040,9 @@ class emitter } #endif // defined(TARGET_RISCV64) + // Used for instrDesc that has relocatable immediate offset + bool iiaSecRel; + } _idAddrUnion; /* Trivial wrappers to return properly typed enums */ diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 540e6e3f8b3107..9bcb574578cc6e 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -5702,7 +5702,15 @@ void emitter::emitIns_R_I(instruction ins, break; } - id = emitNewInstrSC(attr, val); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && EA_IS_CNS_SEC_RELOC(attr)) + { + id = emitNewInstrCns(attr, val); + id->idAddr()->iiaSecRel = true; + } + else + { + id = emitNewInstrSC(attr, val); + } id->idIns(ins); id->idInsFmt(fmt); id->idReg1(reg); @@ -15098,7 +15106,16 @@ BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) if (id->idIsCnsReloc()) { - emitRecordRelocation((void*)(dst - (unsigned)EA_SIZE(size)), (void*)(size_t)val, IMAGE_REL_BASED_MOFFSET); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && id->idAddr()->iiaSecRel) + { + // For section relative, the immediate offset is relocatable and hence need IMAGE_REL_SECREL + emitRecordRelocation((void*)(dst - (unsigned)EA_SIZE(size)), (void*)(size_t)val, IMAGE_REL_SECREL); + } + else + { + emitRecordRelocation((void*)(dst - (unsigned)EA_SIZE(size)), (void*)(size_t)val, + IMAGE_REL_BASED_MOFFSET); + } } goto DONE; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 86caf8654cd9e5..a7cb4569fb09c0 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -10771,6 +10771,8 @@ const char* GenTree::gtGetHandleKindString(GenTreeFlags flags) return "GTF_ICON_FIELD_SEQ"; case GTF_ICON_STATIC_ADDR_PTR: return "GTF_ICON_STATIC_ADDR_PTR"; + case GTF_ICON_SECREL_OFFSET: + return "GTF_ICON_SECREL_OFFSET"; default: return "ILLEGAL!"; } @@ -12000,6 +12002,9 @@ void Compiler::gtDispConst(GenTree* tree) case GTF_ICON_STATIC_ADDR_PTR: printf(" static base addr cell"); break; + case GTF_ICON_SECREL_OFFSET: + printf(" relative offset in section"); + break; default: printf(" UNKNOWN"); break; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index f06b0cd4386716..0902c00c1650c1 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -536,6 +536,7 @@ enum GenTreeFlags : unsigned int GTF_ICON_STATIC_BOX_PTR = 0x10000000, // GT_CNS_INT -- constant is an address of the box for a STATIC_IN_HEAP field GTF_ICON_FIELD_SEQ = 0x11000000, // <--------> -- constant is a FieldSeq* (used only as VNHandle) GTF_ICON_STATIC_ADDR_PTR = 0x13000000, // GT_CNS_INT -- constant is a pointer to a static base address + GTF_ICON_SECREL_OFFSET = 0x14000000, // GT_CNS_INT -- constant is an offset in a certain section. // GTF_ICON_REUSE_REG_VAL = 0x00800000 // GT_CNS_INT -- GTF_REUSE_REG_VAL, defined above GTF_ICON_SIMD_COUNT = 0x00200000, // GT_CNS_INT -- constant is Vector.Count diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 507ee46f479eb5..b3e5bddd9e804d 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -442,7 +442,9 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess() return result; } - if (opts.OptimizationDisabled()) + // Always expand for NativeAOT because the TLS access will not be generated by NativeAOT + const bool isNativeAOT = IsTargetAbi(CORINFO_NATIVEAOT_ABI); + if (!isNativeAOT && opts.OptimizationDisabled()) { JITDUMP("Optimizations aren't allowed - bail out.\n") return result; @@ -457,7 +459,215 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess() return result; } - return fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCall>(true); + return isNativeAOT ? fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCallNativeAOT>( + false /* expand rarely run blocks for NativeAOT */) + : fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCall>(true); +} + +//------------------------------------------------------------------------------ +// fgExpandThreadLocalAccessForCallNativeAOT : Expand the access of tlsRoot needed +// to access fields marked with [ThreadLocal]. +// +// Arguments: +// pBlock - Block containing the helper call to expand. If expansion is performed, +// this is updated to the new block that was an outcome of block splitting. +// stmt - Statement containing the helper call +// call - The helper call +// +// +// Returns: +// true if we expanded any field access, false otherwise. +// +bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call) +{ + assert(IsTargetAbi(CORINFO_NATIVEAOT_ABI)); + BasicBlock* block = *pBlock; + CorInfoHelpFunc helper = call->GetHelperNum(); + + const bool isExpTLSFieldAccess = (helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR); + if (!call->IsHelperCall() || !isExpTLSFieldAccess) + { + return false; + } + + JITDUMP("Expanding thread static local access for [%06d] in " FMT_BB ":\n", dspTreeID(call), block->bbNum); + DISPTREE(call); + JITDUMP("\n"); + + CORINFO_THREAD_STATIC_INFO_NATIVEAOT threadStaticInfo; + memset(&threadStaticInfo, 0, sizeof(CORINFO_THREAD_STATIC_INFO_NATIVEAOT)); + + info.compCompHnd->getThreadLocalStaticInfo_NativeAOT(&threadStaticInfo); + + JITDUMP("tlsRootObject= %p\n", dspPtr(threadStaticInfo.tlsRootObject.addr)); + JITDUMP("tlsIndexObject= %p\n", dspPtr(threadStaticInfo.tlsIndexObject.addr)); + JITDUMP("offsetOfThreadLocalStoragePointer= %u\n", dspOffset(threadStaticInfo.offsetOfThreadLocalStoragePointer)); + JITDUMP("threadStaticBaseSlow= %p\n", dspPtr(threadStaticInfo.threadStaticBaseSlow.addr)); + + // Split block right before the call tree + BasicBlock* prevBb = block; + GenTree** callUse = nullptr; + Statement* newFirstStmt = nullptr; + DebugInfo debugInfo = stmt->GetDebugInfo(); + block = fgSplitBlockBeforeTree(block, stmt, call, &newFirstStmt, &callUse); + *pBlock = block; + var_types callType = call->TypeGet(); + assert(prevBb != nullptr && block != nullptr); + + unsigned finalLclNum = lvaGrabTemp(true DEBUGARG("Final offset")); + // Note, `tlsRoot` refers to the TLS blob object, which is an unpinned managed object, + // thus the type of the local is TYP_REF + lvaTable[finalLclNum].lvType = TYP_REF; + GenTree* finalLcl = gtNewLclVarNode(finalLclNum); + + // Block ops inserted by the split need to be morphed here since we are after morph. + // We cannot morph stmt yet as we may modify it further below, and the morphing + // could invalidate callUse. + while ((newFirstStmt != nullptr) && (newFirstStmt != stmt)) + { + fgMorphStmtBlockOps(block, newFirstStmt); + newFirstStmt = newFirstStmt->GetNextStmt(); + } + + if (TargetOS::IsWindows) + { +#ifdef TARGET_64BIT + // prevBb (BBJ_NONE): [weight: 1.0] + // ... + // + // tlsRootNullCondBB (BBJ_COND): [weight: 1.0] + // fastPathValue = [tlsRootAddress] + // if (fastPathValue != nullptr) + // goto fastPathBb; + // + // fallbackBb (BBJ_ALWAYS): [weight: 0] + // tlsRoot = HelperCall(); + // goto block; + // + // fastPathBb(BBJ_ALWAYS): [weight: 1.0] + // tlsRoot = fastPathValue; + // + // block (...): [weight: 1.0] + // use(tlsRoot); + // ... + + // Mark this ICON as a TLS_HDL, codegen will use FS:[cns] or GS:[cns] + GenTree* tlsValue = gtNewIconHandleNode(threadStaticInfo.offsetOfThreadLocalStoragePointer, GTF_ICON_TLS_HDL); + tlsValue = gtNewIndir(TYP_I_IMPL, tlsValue, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + + CORINFO_CONST_LOOKUP tlsIndexObject = threadStaticInfo.tlsIndexObject; + + GenTree* dllRef = gtNewIconHandleNode((size_t)tlsIndexObject.handle, GTF_ICON_OBJ_HDL); + dllRef = gtNewIndir(TYP_INT, dllRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + dllRef = gtNewOperNode(GT_MUL, TYP_I_IMPL, dllRef, gtNewIconNode(TARGET_POINTER_SIZE, TYP_INT)); + + // Add the dllRef to produce thread local storage reference for coreclr + tlsValue = gtNewOperNode(GT_ADD, TYP_I_IMPL, tlsValue, dllRef); + + // Base of coreclr's thread local storage + tlsValue = gtNewIndir(TYP_I_IMPL, tlsValue, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + + CORINFO_CONST_LOOKUP tlsRootObject = threadStaticInfo.tlsRootObject; + + // This resolves to an offset which is TYP_INT + GenTree* tlsRootOffset = gtNewIconNode((size_t)tlsRootObject.handle, TYP_INT); + tlsRootOffset->gtFlags |= GTF_ICON_SECREL_OFFSET; + + // Add the tlsValue and tlsRootOffset to produce tlsRootAddr. + GenTree* tlsRootAddr = gtNewOperNode(GT_ADD, TYP_I_IMPL, tlsValue, tlsRootOffset); + + // Cache the TlsRootAddr value + unsigned tlsRootAddrLclNum = lvaGrabTemp(true DEBUGARG("TlsRootAddr access")); + lvaTable[tlsRootAddrLclNum].lvType = TYP_I_IMPL; + GenTree* tlsRootAddrDef = gtNewStoreLclVarNode(tlsRootAddrLclNum, tlsRootAddr); + GenTree* tlsRootAddrUse = gtNewLclVarNode(tlsRootAddrLclNum); + + // See comments near finalLclNum above regarding TYP_REF + GenTree* tlsRootVal = gtNewIndir(TYP_REF, tlsRootAddrUse, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + + GenTree* tlsRootDef = gtNewStoreLclVarNode(finalLclNum, tlsRootVal); + + GenTree* tlsRootNullCond = gtNewOperNode(GT_NE, TYP_INT, gtCloneExpr(finalLcl), gtNewIconNode(0, TYP_I_IMPL)); + tlsRootNullCond = gtNewOperNode(GT_JTRUE, TYP_VOID, tlsRootNullCond); + + // tlsRootNullCondBB + BasicBlock* tlsRootNullCondBB = fgNewBBFromTreeAfter(BBJ_COND, prevBb, tlsRootAddrDef, debugInfo); + fgInsertStmtAfter(tlsRootNullCondBB, tlsRootNullCondBB->firstStmt(), fgNewStmtFromTree(tlsRootNullCond)); + fgInsertStmtAfter(tlsRootNullCondBB, tlsRootNullCondBB->firstStmt(), fgNewStmtFromTree(tlsRootDef)); + + CORINFO_CONST_LOOKUP threadStaticSlowHelper = threadStaticInfo.threadStaticBaseSlow; + + // See comments near finalLclNum above regarding TYP_REF + GenTreeCall* slowHelper = + gtNewIndCallNode(gtNewIconHandleNode((size_t)threadStaticSlowHelper.addr, GTF_ICON_TLS_HDL), TYP_REF); + GenTree* helperArg = gtClone(tlsRootAddrUse); + slowHelper->gtArgs.PushBack(this, NewCallArg::Primitive(helperArg)); + fgMorphArgs(slowHelper); + + // fallbackBb + GenTree* fallbackValueDef = gtNewStoreLclVarNode(finalLclNum, slowHelper); + BasicBlock* fallbackBb = + fgNewBBFromTreeAfter(BBJ_ALWAYS, tlsRootNullCondBB, fallbackValueDef, debugInfo, block, true); + + GenTree* fastPathValueDef = gtNewStoreLclVarNode(finalLclNum, gtCloneExpr(finalLcl)); + BasicBlock* fastPathBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, fallbackBb, fastPathValueDef, debugInfo, block, true); + + *callUse = finalLcl; + + fgMorphStmtBlockOps(block, stmt); + gtUpdateStmtSideEffects(stmt); + + // + // Update preds in all new blocks + // + fgAddRefPred(fallbackBb, tlsRootNullCondBB); + fgAddRefPred(fastPathBb, tlsRootNullCondBB); + + fgAddRefPred(block, fallbackBb); + fgAddRefPred(block, fastPathBb); + + tlsRootNullCondBB->SetTrueTarget(fastPathBb); + tlsRootNullCondBB->SetFalseTarget(fallbackBb); + + // Inherit the weights + block->inheritWeight(prevBb); + tlsRootNullCondBB->inheritWeight(prevBb); + fastPathBb->inheritWeight(prevBb); + + // fallback will just execute first time + fallbackBb->bbSetRunRarely(); + + fgRemoveRefPred(block, prevBb); + fgAddRefPred(tlsRootNullCondBB, prevBb); + prevBb->SetTarget(tlsRootNullCondBB); + + // + // Update loop info if loop table is known to be valid + // + tlsRootNullCondBB->bbNatLoopNum = prevBb->bbNatLoopNum; + fastPathBb->bbNatLoopNum = prevBb->bbNatLoopNum; + fallbackBb->bbNatLoopNum = prevBb->bbNatLoopNum; + + // All blocks are expected to be in the same EH region + assert(BasicBlock::sameEHRegion(prevBb, block)); + assert(BasicBlock::sameEHRegion(prevBb, tlsRootNullCondBB)); + assert(BasicBlock::sameEHRegion(prevBb, fastPathBb)); + + JITDUMP("tlsRootNullCondBB: " FMT_BB "\n", tlsRootNullCondBB->bbNum); + JITDUMP("fallbackBb: " FMT_BB "\n", fallbackBb->bbNum); + JITDUMP("fastPathBb: " FMT_BB "\n", fastPathBb->bbNum); +#else + unreached(); + +#endif // TARGET_64BIT + + return true; + } + else + { + unreached(); + } + return false; } //------------------------------------------------------------------------------ @@ -472,7 +682,7 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess() // // // Returns: -// PhaseStatus indicating what, if anything, was changed. +// true if we expanded any field access, false otherwise. // // Notes: // A cache is stored in thread local storage (TLS) of coreclr. It maps the typeIndex (embedded in @@ -485,6 +695,8 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess() // bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call) { + assert(!opts.IsReadyToRun()); + BasicBlock* block = *pBlock; CorInfoHelpFunc helper = call->GetHelperNum(); @@ -495,8 +707,6 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* return false; } - assert(!opts.IsReadyToRun()); - if (TargetOS::IsUnix) { #if defined(TARGET_ARM) || !defined(TARGET_64BIT) @@ -572,19 +782,17 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* fgMorphStmtBlockOps(block, stmt); gtUpdateStmtSideEffects(stmt); - GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); - GenTree* tlsValue = nullptr; - unsigned tlsLclNum = lvaGrabTemp(true DEBUGARG("TLS access")); - lvaTable[tlsLclNum].lvType = TYP_I_IMPL; - GenTree* maxThreadStaticBlocksValue = nullptr; - GenTree* threadStaticBlocksValue = nullptr; - GenTree* tlsValueDef = nullptr; + GenTree* tlsValue = nullptr; + unsigned tlsLclNum = lvaGrabTemp(true DEBUGARG("TLS access")); + lvaTable[tlsLclNum].lvType = TYP_I_IMPL; + GenTree* maxThreadStaticBlocksValue = nullptr; + GenTree* threadStaticBlocksValue = nullptr; + GenTree* tlsValueDef = nullptr; if (TargetOS::IsWindows) { size_t tlsIndexValue = (size_t)threadStaticBlocksInfo.tlsIndex.addr; GenTree* dllRef = nullptr; - if (tlsIndexValue != 0) { dllRef = gtNewIconHandleNode(tlsIndexValue * TARGET_POINTER_SIZE, GTF_ICON_TLS_HDL); @@ -704,6 +912,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* threadStaticBlocksValue = gtNewIndir(TYP_I_IMPL, threadStaticBlocksRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); // Create tree for "if (maxThreadStaticBlocks < typeIndex)" + GenTree* typeThreadStaticBlockIndexValue = call->gtArgs.GetArgByIndex(0)->GetNode(); GenTree* maxThreadStaticBlocksCond = gtNewOperNode(GT_LT, TYP_INT, maxThreadStaticBlocksValue, gtCloneExpr(typeThreadStaticBlockIndexValue)); maxThreadStaticBlocksCond = gtNewOperNode(GT_JTRUE, TYP_VOID, maxThreadStaticBlocksCond); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 2ba0643887987d..a0981b548ecdf4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3822,14 +3822,19 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved case CORINFO_FIELD_STATIC_TLS_MANAGED: - if (pFieldInfo->helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) - { - typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, false); - } - else +#ifdef FEATURE_READYTORUN + if (!opts.IsReadyToRun()) +#endif // FEATURE_READYTORUN { - assert(pFieldInfo->helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED); - typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, true); + if (pFieldInfo->helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED) + { + typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, false); + } + else + { + assert(pFieldInfo->helper == CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED); + typeIndex = info.compCompHnd->getThreadLocalFieldInfo(pResolvedToken->hField, true); + } } FALLTHROUGH; @@ -3847,6 +3852,21 @@ GenTree* Compiler::impImportStaticFieldAddress(CORINFO_RESOLVED_TOKEN* pResolved callFlags |= GTF_CALL_HOISTABLE; } + if (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_TLS_MANAGED) + { + assert(pFieldInfo->helper == CORINFO_HELP_READYTORUN_THREADSTATIC_BASE); + op1 = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR, TYP_BYREF); + + op1->AsCall()->gtInitClsHnd = pResolvedToken->hClass; + op1->AsCall()->setEntryPoint(pFieldInfo->fieldLookup); + op1->gtFlags |= callFlags; + + op1 = gtNewOperNode(GT_ADD, op1->TypeGet(), op1, gtNewIconNode(pFieldInfo->offset, innerFldSeq)); + + m_preferredInitCctor = CORINFO_HELP_READYTORUN_GCSTATIC_BASE; + break; + } + op1 = gtNewHelperCallNode(pFieldInfo->helper, TYP_BYREF); if (pResolvedToken->hClass == info.compClassHnd && m_preferredInitCctor == CORINFO_HELP_UNDEF && (pFieldInfo->helper == CORINFO_HELP_READYTORUN_GCSTATIC_BASE || diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index a906ce2a5e440f..a667cf938b01c0 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -465,6 +465,7 @@ enum emitAttr : unsigned EA_BYREF = EA_BYREF_FLG | EA_PTRSIZE, /* size == -2 */ EA_DSP_RELOC_FLG = 0x400, // Is the displacement of the instruction relocatable? EA_CNS_RELOC_FLG = 0x800, // Is the immediate of the instruction relocatable? + EA_CNS_SEC_RELOC = 0x1000, // Is the offset immediate that should be relocatable }; #define EA_ATTR(x) ((emitAttr)(x)) @@ -481,6 +482,7 @@ enum emitAttr : unsigned #define EA_IS_GCREF_OR_BYREF(x) ((((unsigned)(x)) & ((unsigned)(EA_BYREF_FLG | EA_GCREF_FLG))) != 0) #define EA_IS_DSP_RELOC(x) ((((unsigned)(x)) & ((unsigned)EA_DSP_RELOC_FLG)) != 0) #define EA_IS_CNS_RELOC(x) ((((unsigned)(x)) & ((unsigned)EA_CNS_RELOC_FLG)) != 0) +#define EA_IS_CNS_SEC_RELOC(x) ((((unsigned)(x)) & ((unsigned)EA_CNS_SEC_RELOC)) != 0) #define EA_IS_RELOC(x) (EA_IS_DSP_RELOC(x) || EA_IS_CNS_RELOC(x)) #define EA_TYPE(x) ((emitAttr)(((unsigned)(x)) & ~(EA_OFFSET_FLG | EA_DSP_RELOC_FLG | EA_CNS_RELOC_FLG))) diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 56e7673e5b8dd1..0e27597dfa7253 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1557,6 +1557,7 @@ void HelperCallProperties::init() case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR: case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED: + case CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR: // These do not invoke static class constructors // diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 797abbc7e93ed1..ebfdd52da3b55d 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -12352,6 +12352,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN case VNF_ReadyToRunStaticBaseGC: case VNF_ReadyToRunStaticBaseNonGC: case VNF_ReadyToRunStaticBaseThread: + case VNF_ReadyToRunStaticBaseThreadNoctor: case VNF_ReadyToRunStaticBaseThreadNonGC: case VNF_ReadyToRunGenericStaticBase: case VNF_ReadyToRunIsInstanceOf: @@ -12739,6 +12740,9 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc) case CORINFO_HELP_READYTORUN_THREADSTATIC_BASE: vnf = VNF_ReadyToRunStaticBaseThread; break; + case CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR: + vnf = VNF_ReadyToRunStaticBaseThreadNoctor; + break; case CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE: vnf = VNF_ReadyToRunStaticBaseThreadNonGC; break; diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index f875f3aadaf43d..bb098dca7ab8ce 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -122,6 +122,7 @@ ValueNumFuncDef(GetsharedNongcstaticBaseNoctor, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseGC, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseNonGC, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseThread, 1, false, true, true, false) +ValueNumFuncDef(ReadyToRunStaticBaseThreadNoctor, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunStaticBaseThreadNonGC, 1, false, true, true, false) ValueNumFuncDef(ReadyToRunGenericStaticBase, 2, false, true, true, false) ValueNumFuncDef(GetsharedGcstaticBaseDynamicclass, 2, false, true, true, false) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs index f2bc04fb1a442c..2a9dbe302dac2e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoHelpFunc.cs @@ -187,11 +187,11 @@ which is the right helper to use to allocate an object of a given type. */ CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE, CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE, CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR, - CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS, CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS, + CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, + CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED, /* Debugger */ @@ -238,6 +238,7 @@ which is the right helper to use to allocate an object of a given type. */ CORINFO_HELP_READYTORUN_GCSTATIC_BASE, CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE, CORINFO_HELP_READYTORUN_THREADSTATIC_BASE, + CORINFO_HELP_READYTORUN_THREADSTATIC_BASE_NOCTOR, CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE, CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR, CORINFO_HELP_READYTORUN_GENERIC_HANDLE, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index ae4da22557a0d3..0db8ace5e1d482 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1433,6 +1433,20 @@ private static void _getThreadLocalStaticBlocksInfo(IntPtr thisHandle, IntPtr* p } } + [UnmanagedCallersOnly] + private static void _getThreadLocalStaticInfo_NativeAOT(IntPtr thisHandle, IntPtr* ppException, CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) + { + var _this = GetThis(thisHandle); + try + { + _this.getThreadLocalStaticInfo_NativeAOT(pInfo); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + [UnmanagedCallersOnly] private static byte _isFieldStatic(IntPtr thisHandle, IntPtr* ppException, CORINFO_FIELD_STRUCT_* fldHnd) { @@ -2537,7 +2551,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 171); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2635,81 +2649,82 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[93] = (delegate* unmanaged)&_getFieldInfo; callbacks[94] = (delegate* unmanaged)&_getThreadLocalFieldInfo; callbacks[95] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[96] = (delegate* unmanaged)&_isFieldStatic; - callbacks[97] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[98] = (delegate* unmanaged)&_getBoundaries; - callbacks[99] = (delegate* unmanaged)&_setBoundaries; - callbacks[100] = (delegate* unmanaged)&_getVars; - callbacks[101] = (delegate* unmanaged)&_setVars; - callbacks[102] = (delegate* unmanaged)&_reportRichMappings; - callbacks[103] = (delegate* unmanaged)&_allocateArray; - callbacks[104] = (delegate* unmanaged)&_freeArray; - callbacks[105] = (delegate* unmanaged)&_getArgNext; - callbacks[106] = (delegate* unmanaged)&_getArgType; - callbacks[107] = (delegate* unmanaged)&_getExactClasses; - callbacks[108] = (delegate* unmanaged)&_getArgClass; - callbacks[109] = (delegate* unmanaged)&_getHFAType; - callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[111] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[112] = (delegate* unmanaged)&_getEEInfo; - callbacks[113] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[114] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[115] = (delegate* unmanaged)&_printMethodName; - callbacks[116] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[117] = (delegate* unmanaged)&_getMethodHash; - callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[119] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[120] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[121] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[122] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[123] = (delegate* unmanaged)&_getHelperFtn; - callbacks[124] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[125] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[126] = (delegate* unmanaged)&_getMethodSync; - callbacks[127] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[128] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[129] = (delegate* unmanaged)&_embedClassHandle; - callbacks[130] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[131] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[132] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[133] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[134] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[135] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[136] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[137] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[138] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[139] = (delegate* unmanaged)&_getCallInfo; - callbacks[140] = (delegate* unmanaged)&_getClassDomainID; - callbacks[141] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[142] = (delegate* unmanaged)&_getObjectContent; - callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[149] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[150] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[151] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[152] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[153] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[154] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[155] = (delegate* unmanaged)&_allocMem; - callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocGCInfo; - callbacks[159] = (delegate* unmanaged)&_setEHcount; - callbacks[160] = (delegate* unmanaged)&_setEHinfo; - callbacks[161] = (delegate* unmanaged)&_logMsg; - callbacks[162] = (delegate* unmanaged)&_doAssert; - callbacks[163] = (delegate* unmanaged)&_reportFatalError; - callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[166] = (delegate* unmanaged)&_recordCallSite; - callbacks[167] = (delegate* unmanaged)&_recordRelocation; - callbacks[168] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[169] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[170] = (delegate* unmanaged)&_getJitFlags; + callbacks[96] = (delegate* unmanaged)&_getThreadLocalStaticInfo_NativeAOT; + callbacks[97] = (delegate* unmanaged)&_isFieldStatic; + callbacks[98] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[99] = (delegate* unmanaged)&_getBoundaries; + callbacks[100] = (delegate* unmanaged)&_setBoundaries; + callbacks[101] = (delegate* unmanaged)&_getVars; + callbacks[102] = (delegate* unmanaged)&_setVars; + callbacks[103] = (delegate* unmanaged)&_reportRichMappings; + callbacks[104] = (delegate* unmanaged)&_allocateArray; + callbacks[105] = (delegate* unmanaged)&_freeArray; + callbacks[106] = (delegate* unmanaged)&_getArgNext; + callbacks[107] = (delegate* unmanaged)&_getArgType; + callbacks[108] = (delegate* unmanaged)&_getExactClasses; + callbacks[109] = (delegate* unmanaged)&_getArgClass; + callbacks[110] = (delegate* unmanaged)&_getHFAType; + callbacks[111] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[112] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[113] = (delegate* unmanaged)&_getEEInfo; + callbacks[114] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[115] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[116] = (delegate* unmanaged)&_printMethodName; + callbacks[117] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[118] = (delegate* unmanaged)&_getMethodHash; + callbacks[119] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[120] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[121] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[122] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[123] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[124] = (delegate* unmanaged)&_getHelperFtn; + callbacks[125] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[126] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[127] = (delegate* unmanaged)&_getMethodSync; + callbacks[128] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[129] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[130] = (delegate* unmanaged)&_embedClassHandle; + callbacks[131] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[132] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[133] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[134] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[135] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[136] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[137] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[138] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[139] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[140] = (delegate* unmanaged)&_getCallInfo; + callbacks[141] = (delegate* unmanaged)&_getClassDomainID; + callbacks[142] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[143] = (delegate* unmanaged)&_getObjectContent; + callbacks[144] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[145] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[146] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[147] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[148] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[149] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[150] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[151] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[152] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[153] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[154] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[155] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[156] = (delegate* unmanaged)&_allocMem; + callbacks[157] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[158] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[159] = (delegate* unmanaged)&_allocGCInfo; + callbacks[160] = (delegate* unmanaged)&_setEHcount; + callbacks[161] = (delegate* unmanaged)&_setEHinfo; + callbacks[162] = (delegate* unmanaged)&_logMsg; + callbacks[163] = (delegate* unmanaged)&_doAssert; + callbacks[164] = (delegate* unmanaged)&_reportFatalError; + callbacks[165] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[166] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[167] = (delegate* unmanaged)&_recordCallSite; + callbacks[168] = (delegate* unmanaged)&_recordRelocation; + callbacks[169] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[170] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[171] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index b75bf623f346f9..394a7a4bbac91b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1109,7 +1109,7 @@ public enum CORINFO_FIELD_ACCESSOR CORINFO_FIELD_STATIC_READYTORUN_HELPER, // static field access using a runtime lookup helper CORINFO_FIELD_STATIC_RELOCATABLE, // static field access from the data segment CORINFO_FIELD_INTRINSIC_ZERO, // intrinsic zero (IntPtr.Zero, UIntPtr.Zero) - CORINFO_FIELD_INTRINSIC_EMPTY_STRING, // intrinsic emptry string (String.Empty) + CORINFO_FIELD_INTRINSIC_EMPTY_STRING, // intrinsic empty string (String.Empty) CORINFO_FIELD_INTRINSIC_ISLITTLEENDIAN, // intrinsic BitConverter.IsLittleEndian } @@ -1157,6 +1157,15 @@ public unsafe struct CORINFO_THREAD_STATIC_BLOCKS_INFO public uint offsetOfGCDataPointer; }; + + public unsafe struct CORINFO_THREAD_STATIC_INFO_NATIVEAOT + { + public uint offsetOfThreadLocalStoragePointer; + public CORINFO_CONST_LOOKUP tlsRootObject; + public CORINFO_CONST_LOOKUP tlsIndexObject; + public CORINFO_CONST_LOOKUP threadStaticBaseSlow; + }; + // System V struct passing // The Classification types are described in the ABI spec at https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf public enum SystemVClassificationType : byte diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 91808b2878b125..1fb0f057d915ba 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -87,6 +87,7 @@ CORINFO_GENERICHANDLE_RESULT*,ref CORINFO_GENERICHANDLE_RESULT CORINFO_METHOD_INFO*,CORINFO_METHOD_INFO* CORINFO_FIELD_INFO*,CORINFO_FIELD_INFO* CORINFO_THREAD_STATIC_BLOCKS_INFO*,CORINFO_THREAD_STATIC_BLOCKS_INFO* +CORINFO_THREAD_STATIC_INFO_NATIVEAOT*,CORINFO_THREAD_STATIC_INFO_NATIVEAOT* CORINFO_CALL_INFO*,CORINFO_CALL_INFO* CORINFO_DEVIRTUALIZATION_INFO*,CORINFO_DEVIRTUALIZATION_INFO* CORINFO_TYPE_LAYOUT_NODE*,CORINFO_TYPE_LAYOUT_NODE* @@ -257,6 +258,7 @@ FUNCTIONS void getFieldInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags, CORINFO_FIELD_INFO* pResult) uint32_t getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isGCtype) void getThreadLocalStaticBlocksInfo (CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType) + void getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) bool isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) int getArrayOrStringLength(CORINFO_OBJECT_HANDLE objHnd) void getBoundaries(CORINFO_METHOD_HANDLE ftn, unsigned int* cILOffsets, uint32_t** pILOffsets, ICorDebugInfo::BoundaryTypes* implicitBoundaries) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index d2e82bc46813e8..5e3674acd39123 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -1342,7 +1342,7 @@ public string GetSymbolAlternateName(ISymbolNode node) protected internal TypeManagerIndirectionNode TypeManagerIndirection = new TypeManagerIndirectionNode(); - protected internal TlsRootNode TlsRoot = new TlsRootNode(); + public TlsRootNode TlsRoot = new TlsRootNode(); public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 95cb2e5ae2efee..d49b7c2997b65c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -237,47 +237,7 @@ private static void EmitInlineTLSAccess(NodeFactory factory, ref X64Emitter enco // bool isInitialExecutable = factory.CompilationModuleGroup.IsSingleFileCompilation; bool isInitialExecutable = false; - if (factory.Target.IsWindows) - { - if (isInitialExecutable) - { - // mov rax,qword ptr gs:[58h] - encoder.Builder.EmitBytes(new byte[] { 0x65, 0x48, 0x8B, 0x04, 0x25, 0x58, 0x00, 0x00, 0x00 }); - - // mov ecx, SECTIONREL tlsRoot - encoder.Builder.EmitBytes(new byte[] { 0xB9 }); - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_SECREL); - - // add rcx,qword ptr [rax] - encoder.Builder.EmitBytes(new byte[] { 0x48, 0x03, 0x08 }); - } - else - { - // mov ecx,dword ptr [_tls_index] - encoder.Builder.EmitBytes(new byte[] { 0x8B, 0x0D }); - encoder.Builder.EmitReloc(factory.ExternSymbol("_tls_index"), RelocType.IMAGE_REL_BASED_REL32); - - // mov rax,qword ptr gs:[58h] - encoder.Builder.EmitBytes(new byte[] { 0x65, 0x48, 0x8B, 0x04, 0x25, 0x58, 0x00, 0x00, 0x00 }); - - // mov rax,qword ptr [rax+rcx*8] - encoder.Builder.EmitBytes(new byte[] { 0x48, 0x8B, 0x04, 0xC8 }); - - // mov ecx, SECTIONREL tlsRoot - encoder.Builder.EmitBytes(new byte[] { 0xB9 }); - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_SECREL); - - // add rcx,rax - encoder.Builder.EmitBytes(new byte[] { 0x48, 0x01, 0xC1 }); - } - - // mov rax, qword ptr[rcx] - encoder.Builder.EmitBytes(new byte[] { 0x48, 0x8b, 0x01 }); - encoder.EmitCompareToZero(Register.RAX); - encoder.EmitJE(getInlinedThreadStaticBaseSlow); - encoder.EmitRET(); - } - else if (factory.Target.OperatingSystem == TargetOS.Linux) + if (factory.Target.OperatingSystem == TargetOS.Linux) { if (isInitialExecutable) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index d0b5413f985e0a..f7527c96dd9ddb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -3366,5 +3366,12 @@ private bool notifyMethodInfoUsage(CORINFO_METHOD_STRUCT_* ftn) // stable e.g. mark calls as no-return if their IL has no rets. return _compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(method); } + +#pragma warning disable CA1822 // Mark members as static + private void getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) + { + // Implemented for NativeAOT only for now. + } +#pragma warning restore CA1822 // Mark members as static } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index f4b46156369c94..b00f3f00a47372 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2140,6 +2140,22 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET } else if (field.IsThreadStatic) { + if (MethodBeingCompiled.Context.Target.IsWindows && MethodBeingCompiled.Context.Target.Architecture == TargetArchitecture.X64) + { + ISortableSymbolNode index = _compilation.NodeFactory.TypeThreadStaticIndex((MetadataType)field.OwningType); + if (index is TypeThreadStaticIndexNode ti) + { + if (ti.IsInlined) + { + fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_TLS_MANAGED; + if (_compilation.HasLazyStaticConstructor(field.OwningType)) + { + fieldFlags |= CORINFO_FIELD_FLAGS.CORINFO_FLG_FIELD_INITCLASS; + } + } + } + } + pResult->helper = CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADSTATIC_BASE; helperId = ReadyToRunHelperId.GetThreadStaticBase; } @@ -2389,6 +2405,14 @@ private bool getStaticBaseAddress(CORINFO_CLASS_STRUCT_* cls, bool isGc, ref COR return true; } + private void getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) + { + pInfo->offsetOfThreadLocalStoragePointer = (uint)(11 * PointerSize); // Offset of ThreadLocalStoragePointer in the TEB + pInfo->tlsIndexObject = CreateConstLookupToSymbol(_compilation.NodeFactory.ExternSymbol("_tls_index")); + pInfo->tlsRootObject = CreateConstLookupToSymbol(_compilation.NodeFactory.TlsRoot); + pInfo->threadStaticBaseSlow = CreateConstLookupToSymbol(_compilation.NodeFactory.HelperEntrypoint(HelperEntrypoint.GetInlinedThreadStaticBaseSlow)); + } + #pragma warning disable CA1822 // Mark members as static private bool notifyMethodInfoUsage(CORINFO_METHOD_STRUCT_* ftn) #pragma warning restore CA1822 // Mark members as static diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 0a9fce19e412af..995f312eafd07e 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -107,6 +107,7 @@ struct JitInterfaceCallbacks void (* getFieldInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, CORINFO_ACCESS_FLAGS flags, CORINFO_FIELD_INFO* pResult); uint32_t (* getThreadLocalFieldInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, bool isGCtype); void (* getThreadLocalStaticBlocksInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType); + void (* getThreadLocalStaticInfo_NativeAOT)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo); bool (* isFieldStatic)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE fldHnd); int (* getArrayOrStringLength)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_OBJECT_HANDLE objHnd); void (* getBoundaries)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, unsigned int* cILOffsets, uint32_t** pILOffsets, ICorDebugInfo::BoundaryTypes* implicitBoundaries); @@ -1135,6 +1136,14 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } + virtual void getThreadLocalStaticInfo_NativeAOT( + CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + CorInfoExceptionClass* pException = nullptr; + _callbacks->getThreadLocalStaticInfo_NativeAOT(_thisHandle, &pException, pInfo); + if (pException != nullptr) throw pException; +} + virtual bool isFieldStatic( CORINFO_FIELD_HANDLE fldHnd) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index ec8f9a9b04110c..94cfa8d2ee0064 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -533,6 +533,21 @@ struct Agnostic_GetThreadLocalStaticBlocksInfo DWORD offsetOfGCDataPointer; }; +struct Agnostic_GetThreadStaticInfo_NativeAOT +{ + DWORD offsetOfThreadLocalStoragePointer; + Agnostic_CORINFO_CONST_LOOKUP tlsRootObject; + Agnostic_CORINFO_CONST_LOOKUP tlsIndexObject; + Agnostic_CORINFO_CONST_LOOKUP threadStaticBaseSlow; +}; + +struct Agnostic_GetClassCtorInitializationInfo +{ + Agnostic_CORINFO_CONST_LOOKUP addr; + Agnostic_CORINFO_CONST_LOOKUP targetAddr; + int size; +}; + struct Agnostic_GetThreadLocalFieldInfo { DWORD staticBlockIndex; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 70c754aef50b5f..7cdb9d5ebede61 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -39,6 +39,7 @@ LWM(EmbedMethodHandle, DWORDLONG, DLDL) LWM(EmbedModuleHandle, DWORDLONG, DLDL) LWM(GetThreadLocalFieldInfo, DLD, DWORD) LWM(GetThreadLocalStaticBlocksInfo, DWORD, Agnostic_GetThreadLocalStaticBlocksInfo) +LWM(GetThreadLocalStaticInfo_NativeAOT, DWORDLONG, Agnostic_GetThreadStaticInfo_NativeAOT) DENSELWM(EmptyStringLiteral, DLD) DENSELWM(ErrorList, DWORD) LWM(FindCallSiteSig, Agnostic_FindCallSiteSig, Agnostic_CORINFO_SIG_INFO) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index a6d49f08219b5b..efb7b7e97461ae 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3657,6 +3657,48 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC pInfo->offsetOfGCDataPointer = value.offsetOfGCDataPointer; } +void MethodContext::recGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + if (GetThreadLocalStaticInfo_NativeAOT == nullptr) + GetThreadLocalStaticInfo_NativeAOT = new LightWeightMap(); + + Agnostic_GetThreadStaticInfo_NativeAOT value; + ZeroMemory(&value, sizeof(value)); + value.tlsRootObject = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->tlsRootObject); + value.tlsIndexObject = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->tlsIndexObject); + value.offsetOfThreadLocalStoragePointer = pInfo->offsetOfThreadLocalStoragePointer; + value.threadStaticBaseSlow = SpmiRecordsHelper::StoreAgnostic_CORINFO_CONST_LOOKUP(&pInfo->threadStaticBaseSlow); + + DWORDLONG key = 1; + + GetThreadLocalStaticInfo_NativeAOT->Add(key, value); + DEBUG_REC(dmpGetThreadLocalStaticInfo_NativeAOT(key, result)); +} +void MethodContext::dmpGetThreadLocalStaticInfo_NativeAOT(DWORDLONG key, + const Agnostic_GetThreadStaticInfo_NativeAOT& value) +{ + printf("GetThreadLocalStaticInfo_NativeAOT key %016" PRIX64 ", tlsRootObject-%s, tlsIndexObject-%s, offsetOfThreadLocalStoragePointer-%u, " + "threadStaticBaseSlow-%s", + key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsRootObject).c_str(), + SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndexObject).c_str(), + value.offsetOfThreadLocalStoragePointer, + SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.threadStaticBaseSlow).c_str()); +} + +void MethodContext::repGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + DWORDLONG key = 1; + Agnostic_GetThreadStaticInfo_NativeAOT value = + LookupByKeyOrMiss(GetThreadLocalStaticInfo_NativeAOT, key, ": key %016" PRIX64 "", key); + + DEBUG_REP(dmpGetThreadLocalStaticInfo_NativeAOT(key, value)); + + pInfo->tlsRootObject = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.tlsRootObject); + pInfo->tlsIndexObject = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.tlsIndexObject); + pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer; + pInfo->threadStaticBaseSlow = SpmiRecordsHelper::RestoreCORINFO_CONST_LOOKUP(value.threadStaticBaseSlow); +} + void MethodContext::recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void** ppIndirection, CORINFO_METHOD_HANDLE result) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index c9465f2dc5082d..ccf42bc0feb0b2 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -482,6 +482,10 @@ class MethodContext void dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_GetThreadLocalStaticBlocksInfo& value); void repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo, bool isGCType); + void recGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo); + void dmpGetThreadLocalStaticInfo_NativeAOT(DWORDLONG key, const Agnostic_GetThreadStaticInfo_NativeAOT& value); + void repGetThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo); + void recEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void** ppIndirection, CORINFO_METHOD_HANDLE result); void dmpEmbedMethodHandle(DWORDLONG key, DLDL value); CORINFO_METHOD_HANDLE repEmbedMethodHandle(CORINFO_METHOD_HANDLE handle, void** ppIndirection); @@ -1138,11 +1142,12 @@ enum mcPackets Packet_GetStaticBaseAddress = 206, Packet_GetThreadLocalFieldInfo = 207, Packet_GetThreadLocalStaticBlocksInfo = 208, - Packet_GetRISCV64PassStructInRegisterFlags = 209, - Packet_GetObjectContent = 210, - Packet_GetTypeLayout = 211, - Packet_HaveSameMethodDefinition = 212, - Packet_NotifyMethodInfoUsage = 213, + Packet_GetThreadLocalStaticInfo_NativeAOT = 209, + Packet_GetRISCV64PassStructInRegisterFlags = 210, + Packet_GetObjectContent = 211, + Packet_GetTypeLayout = 212, + Packet_HaveSameMethodDefinition = 213, + Packet_NotifyMethodInfoUsage = 214, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index d0e7b69cdb8c38..067adc0e8808f3 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1080,6 +1080,13 @@ void interceptor_ICJI::getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC mc->recGetThreadLocalStaticBlocksInfo(pInfo, isGCType); } +void interceptor_ICJI::getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + mc->cr->AddCall("getThreadLocalStaticInfo_NativeAOT"); + original_ICorJitInfo->getThreadLocalStaticInfo_NativeAOT(pInfo); + mc->recGetThreadLocalStaticInfo_NativeAOT(pInfo); +} + // Returns true iff "fldHnd" represents a static field. bool interceptor_ICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index ddce7e7e18241e..9fcc2a17f8a452 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -777,6 +777,13 @@ void interceptor_ICJI::getThreadLocalStaticBlocksInfo( original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo, isGCType); } +void interceptor_ICJI::getThreadLocalStaticInfo_NativeAOT( + CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + mcs->AddCall("getThreadLocalStaticInfo_NativeAOT"); + original_ICorJitInfo->getThreadLocalStaticInfo_NativeAOT(pInfo); +} + bool interceptor_ICJI::isFieldStatic( CORINFO_FIELD_HANDLE fldHnd) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 4577a26e2292fb..394746a7a200a1 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -681,6 +681,12 @@ void interceptor_ICJI::getThreadLocalStaticBlocksInfo( original_ICorJitInfo->getThreadLocalStaticBlocksInfo(pInfo, isGCType); } +void interceptor_ICJI::getThreadLocalStaticInfo_NativeAOT( + CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + original_ICorJitInfo->getThreadLocalStaticInfo_NativeAOT(pInfo); +} + bool interceptor_ICJI::isFieldStatic( CORINFO_FIELD_HANDLE fldHnd) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index f2e2305dc45aa6..89c7f4e6273779 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -897,6 +897,12 @@ void MyICJI::getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* p jitInstance->mc->repGetThreadLocalStaticBlocksInfo(pInfo, isGCType); } +void MyICJI::getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + jitInstance->mc->cr->AddCall("getThreadLocalStaticInfo_NativeAOT"); + jitInstance->mc->repGetThreadLocalStaticInfo_NativeAOT(pInfo); +} + // Returns true iff "fldHnd" represents a static field. bool MyICJI::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index ca1dd982e1cbb0..816b393ec89de2 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1293,6 +1293,13 @@ static CorInfoHelpFunc getInstanceFieldHelper(FieldDesc * pField, CORINFO_ACCESS /*********************************************************************/ + +void CEEInfo::getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIVEAOT* pInfo) +{ + LIMITED_METHOD_CONTRACT; + UNREACHABLE(); // only called with NativeAOT. +} + uint32_t CEEInfo::getThreadLocalFieldInfo (CORINFO_FIELD_HANDLE field, bool isGCType) { CONTRACTL { From f5a97b2656615368677d937b35d944a62876bef3 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Wed, 17 Jan 2024 13:57:35 -0800 Subject: [PATCH 084/189] Fix Options Source Gen RangeAttribute Thread Safety (#97045) * Fix Options Source Gen RangeAttribute Thread Safety * Address the feedback * More feedback addressing * Feedback++ --- .../gen/Emitter.cs | 83 ++++++++++--------- .../EmitterWithCustomValidator.netcore.g.cs | 53 +++++++----- .../EmitterWithCustomValidator.netfx.g.cs | 53 +++++++----- ...eneratedAttributesTest.netcore.lang10.g.cs | 71 +++++++++------- ...eneratedAttributesTest.netcore.lang11.g.cs | 71 +++++++++------- .../GeneratedAttributesTest.netfx.lang10.g.cs | 71 +++++++++------- .../GeneratedAttributesTest.netfx.lang11.g.cs | 71 +++++++++------- ...tionsExtendingSystemClassTest.netcore.g.cs | 53 +++++++----- ...OptionsExtendingSystemClassTest.netfx.g.cs | 53 +++++++----- .../OptionsRuntimeTests.cs | 32 +++++++ .../Baselines/NetCoreApp/Validators.g.cs | 73 ++++++++-------- .../Baselines/NetFX/Validators.g.cs | 73 ++++++++-------- 12 files changed, 433 insertions(+), 324 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs index 4cb76b6b4908b2..d12b13d8c3ed9c 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs @@ -382,26 +382,26 @@ public void EmitRangeAttribute(string modifier, string prefix, string className, string initializationString = emitTimeSpanSupport ? """ - if (OperandType == typeof(global::System.TimeSpan)) - { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } """ : """ - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); """; string convertValue = emitTimeSpanSupport ? @@ -470,7 +470,7 @@ public void EmitRangeAttribute(string modifier, string prefix, string className, public {{qualifiedClassName}}(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -483,33 +483,40 @@ public void EmitRangeAttribute(string modifier, string prefix, string className, public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) + lock (_lock) { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; {{initializationString}} + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs index 38bacf966df052..b36fff7e49060b 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs @@ -84,7 +84,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -97,34 +97,41 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs index fe77e3e6bd924a..1fa2f9c2e25770 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs @@ -82,7 +82,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -95,34 +95,41 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs index 7cf1fe61e1a94b..789d299cf93c5a 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs @@ -410,7 +410,7 @@ public __SourceGen__2C497155_RangeAttribute(double minimum, double maximum) : ba public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -423,47 +423,54 @@ public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string min public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs index f7bba046033420..60d511f2e83561 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs @@ -410,7 +410,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -423,47 +423,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs index 4b28eb159d147b..9c20532b17631d 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs @@ -325,7 +325,7 @@ public __SourceGen__2C497155_RangeAttribute(double minimum, double maximum) : ba public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -338,47 +338,54 @@ public __SourceGen__2C497155_RangeAttribute(global::System.Type type, string min public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs index 4c300abc6d05bc..c563c65e821903 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs @@ -325,7 +325,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -338,47 +338,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netcore.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netcore.g.cs index 93a0f56025c569..00e0de4d51f4d0 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netcore.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netcore.g.cs @@ -185,7 +185,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -198,34 +198,41 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netfx.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netfx.g.cs index 9323bae7584c48..c7a038cc2d6cf6 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netfx.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/OptionsExtendingSystemClassTest.netfx.g.cs @@ -179,7 +179,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -192,34 +192,41 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) + lock (_lock) { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) - { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + if (!_initialized) + { + if (Minimum is null || Maximum is null) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; + } } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs index 4c701e4b9f498f..761a765752f5f6 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/OptionsRuntimeTests.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -399,6 +400,23 @@ public void TestCustomGeneratedAttributes() Assert.Equal(results.Count(), generatorResult.Failures.Count()); } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public void TestGeneratedRangeAttributeThreadSafety() + { + OptionsWithTimeSpanRangeAttribute options = new OptionsWithTimeSpanRangeAttribute() { Name = "T1", Period = TimeSpan.FromHours(1) }; + TimeSpanRangeAttributeValidator validator = new TimeSpanRangeAttributeValidator(); + + var barrier = new Barrier(8); + Task.WaitAll( + (from i in Enumerable.Range(0, barrier.ParticipantCount) + select Task.Factory.StartNew(() => + { + barrier.SignalAndWait(); + ValidateOptionsResult result = validator.Validate("T1", options); + Assert.True(result.Succeeded); + }, TaskCreationOptions.LongRunning)).ToArray()); + } } public class FakeCount(int count) { public int Count { get { return count; } } } @@ -605,4 +623,18 @@ public partial class NewAttributesValidator : IValidateOptions + { + } } diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs index 6d96a3fecbd52c..8e230835246dcd 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs @@ -1,4 +1,4 @@ - + // #nullable enable #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103 @@ -2225,7 +2225,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -2238,47 +2238,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) + lock (_lock) { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs index ce4d0f3432205e..e193e6fc7ea3ad 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs @@ -1,4 +1,4 @@ - + // #nullable enable #pragma warning disable CS1591 // Compensate for https://github.com/dotnet/roslyn/issues/54103 @@ -2115,7 +2115,7 @@ public __SourceGen__RangeAttribute(double minimum, double maximum) : base() public __SourceGen__RangeAttribute(global::System.Type type, string minimum, string maximum) : base() { OperandType = type; - NeedToConvertMinMax = true; + _needToConvertMinMax = true; Minimum = minimum; Maximum = maximum; } @@ -2128,47 +2128,54 @@ public __SourceGen__RangeAttribute(global::System.Type type, string minimum, str public bool ConvertValueInInvariantCulture { get; set; } public override string FormatErrorMessage(string name) => string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum); - private bool NeedToConvertMinMax { get; } - private bool Initialized { get; set; } - private const string c_minMaxError = "The minimum and maximum values must be set to valid values."; + private readonly bool _needToConvertMinMax; + private volatile bool _initialized; + private readonly object _lock = new(); + private const string MinMaxError = "The minimum and maximum values must be set to valid values."; public override bool IsValid(object? value) { - if (!Initialized) + if (!_initialized) { - if (Minimum is null || Maximum is null) - { - throw new global::System.InvalidOperationException(c_minMaxError); - } - if (NeedToConvertMinMax) + lock (_lock) { - System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; - if (OperandType == typeof(global::System.TimeSpan)) + if (!_initialized) { - if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || - !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + if (Minimum is null || Maximum is null) { - throw new global::System.InvalidOperationException(c_minMaxError); + throw new global::System.InvalidOperationException(MinMaxError); } - Minimum = timeSpanMinimum; - Maximum = timeSpanMaximum; - } - else - { - Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); - Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError); + if (_needToConvertMinMax) + { + System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture; + if (OperandType == typeof(global::System.TimeSpan)) + { + if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) || + !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum)) + { + throw new global::System.InvalidOperationException(MinMaxError); + } + Minimum = timeSpanMinimum; + Maximum = timeSpanMaximum; + } + else + { + Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(MinMaxError); + } + } + int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); + if (cmp > 0) + { + throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); + } + else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) + { + throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); + } + _initialized = true; } } - int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum); - if (cmp > 0) - { - throw new global::System.InvalidOperationException("The maximum value '{Maximum}' must be greater than or equal to the minimum value '{Minimum}'."); - } - else if (cmp == 0 && (MinimumIsExclusive || MaximumIsExclusive)) - { - throw new global::System.InvalidOperationException("Cannot use exclusive bounds when the maximum value is equal to the minimum value."); - } - Initialized = true; } if (value is null or string { Length: 0 }) From 473a9833754d0ccf3499f26988c469ea770632ef Mon Sep 17 00:00:00 2001 From: DeepakRajendrakumaran Date: Wed, 17 Jan 2024 14:06:46 -0800 Subject: [PATCH 085/189] Updating Sum() implementation for Vector128 and Vector256 + adding lowering for Vector512 (#95568) * Updating Sum() implementation. * Fixing codegen * Addressing review comments. * Fix Formatting * Enabling for long on x86. * Cleaning up ToScalar implementation --- src/coreclr/jit/compiler.h | 4 + src/coreclr/jit/gentree.cpp | 173 ++++++++++++++++++++----- src/coreclr/jit/hwintrinsiclistxarch.h | 1 + src/coreclr/jit/hwintrinsicxarch.cpp | 34 ++--- 4 files changed, 152 insertions(+), 60 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 90dd74a9a92edf..411a14976907bf 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3271,6 +3271,10 @@ class Compiler GenTree* op4, CorInfoType simdBaseJitType, unsigned simdSize); + GenTree* gtNewSimdToScalarNode(var_types type, + GenTree* op1, + CorInfoType simdBaseJitType, + unsigned simdSize); #endif // TARGET_XARCH GenTree* gtNewSimdUnOpNode(genTreeOps op, diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a7cb4569fb09c0..0ad8c48afc106b 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -24491,58 +24491,113 @@ GenTree* Compiler::gtNewSimdSumNode(var_types type, GenTree* op1, CorInfoType si GenTree* tmp = nullptr; #if defined(TARGET_XARCH) - assert(!varTypeIsByte(simdBaseType) && !varTypeIsLong(simdBaseType)); - assert(simdSize != 64); - // HorizontalAdd combines pairs so we need log2(vectorLength) passes to sum all elements together. - unsigned vectorLength = getSIMDVectorLength(simdSize, simdBaseType); - int haddCount = genLog2(vectorLength); + if (simdSize == 64) + { + assert(IsBaselineVector512IsaSupportedDebugOnly()); + GenTree* op1Dup = fgMakeMultiUse(&op1); + op1 = gtNewSimdGetUpperNode(TYP_SIMD32, op1, simdBaseJitType, simdSize); + op1Dup = gtNewSimdGetLowerNode(TYP_SIMD32, op1Dup, simdBaseJitType, simdSize); + simdSize = simdSize / 2; + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD32, op1, op1Dup, simdBaseJitType, simdSize); + } if (simdSize == 32) { - // Minus 1 because for the last pass we split the vector to low / high and add them together. - haddCount -= 1; + assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); + GenTree* op1Dup = fgMakeMultiUse(&op1); + op1 = gtNewSimdGetUpperNode(TYP_SIMD16, op1, simdBaseJitType, simdSize); + op1Dup = gtNewSimdGetLowerNode(TYP_SIMD16, op1Dup, simdBaseJitType, simdSize); + simdSize = simdSize / 2; + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD16, op1, op1Dup, simdBaseJitType, simdSize); + } - if (varTypeIsFloating(simdBaseType)) + assert(simdSize == 16); + + if (varTypeIsFloating(simdBaseType)) + { + if (simdBaseType == TYP_FLOAT) { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - intrinsic = NI_AVX_HorizontalAdd; + assert(compIsaSupportedDebugOnly(InstructionSet_SSE2)); + GenTree* op1Shuffled = fgMakeMultiUse(&op1); + if (compOpportunisticallyDependsOn(InstructionSet_AVX)) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + // The permute below gives us [0, 1, 2, 3] -> [1, 0, 3, 2] + op1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode((int)0b10110001, TYP_INT), NI_AVX_Permute, + simdBaseJitType, simdSize); + // The add below now results in [0 + 1, 1 + 0, 2 + 3, 3 + 2] + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD16, op1, op1Shuffled, simdBaseJitType, simdSize); + op1Shuffled = fgMakeMultiUse(&op1); + // The permute below gives us [0 + 1, 1 + 0, 2 + 3, 3 + 2] -> [2 + 3, 3 + 2, 0 + 1, 1 + 0] + op1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode((int)0b01001110, TYP_INT), NI_AVX_Permute, + simdBaseJitType, simdSize); + } + else + { + assert(compIsaSupportedDebugOnly(InstructionSet_SSE)); + // The shuffle below gives us [0, 1, 2, 3] -> [1, 0, 3, 2] + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op1Shuffled, gtNewIconNode((int)0b10110001, TYP_INT), + NI_SSE_Shuffle, simdBaseJitType, simdSize); + op1Shuffled = fgMakeMultiUse(&op1Shuffled); + // The add below now results in [0 + 1, 1 + 0, 2 + 3, 3 + 2] + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD16, op1, op1Shuffled, simdBaseJitType, simdSize); + op1Shuffled = fgMakeMultiUse(&op1); + // The shuffle below gives us [0 + 1, 1 + 0, 2 + 3, 3 + 2] -> [2 + 3, 3 + 2, 0 + 1, 1 + 0] + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op1Shuffled, gtNewIconNode((int)0b01001110, TYP_INT), + NI_SSE_Shuffle, simdBaseJitType, simdSize); + op1Shuffled = fgMakeMultiUse(&op1Shuffled); + } + // Finally adding the results gets us [(0 + 1) + (2 + 3), (1 + 0) + (3 + 2), (2 + 3) + (0 + 1), (3 + 2) + (1 + // + 0)] + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD16, op1, op1Shuffled, simdBaseJitType, simdSize); + return gtNewSimdToScalarNode(type, op1, simdBaseJitType, simdSize); } else { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_HorizontalAdd; + assert(compIsaSupportedDebugOnly(InstructionSet_SSE2)); + GenTree* op1Shuffled = fgMakeMultiUse(&op1); + if (compOpportunisticallyDependsOn(InstructionSet_AVX)) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + // The permute below gives us [0, 1] -> [1, 0] + op1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode((int)0b0001, TYP_INT), NI_AVX_Permute, + simdBaseJitType, simdSize); + } + else + { + // The shuffle below gives us [0, 1] -> [1, 0] + op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op1Shuffled, gtNewIconNode((int)0b0001, TYP_INT), + NI_SSE2_Shuffle, simdBaseJitType, simdSize); + op1Shuffled = fgMakeMultiUse(&op1Shuffled); + } + // Finally adding the results gets us [0 + 1, 1 + 0] + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD16, op1, op1Shuffled, simdBaseJitType, simdSize); + return gtNewSimdToScalarNode(type, op1, simdBaseJitType, simdSize); } } - else if (varTypeIsFloating(simdBaseType)) - { - assert(compIsaSupportedDebugOnly(InstructionSet_SSE3)); - intrinsic = NI_SSE3_HorizontalAdd; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_SSSE3)); - intrinsic = NI_SSSE3_HorizontalAdd; - } - for (int i = 0; i < haddCount; i++) - { - tmp = fgMakeMultiUse(&op1); - op1 = gtNewSimdHWIntrinsicNode(simdType, op1, tmp, intrinsic, simdBaseJitType, simdSize); - } + unsigned vectorLength = getSIMDVectorLength(simdSize, simdBaseType); + int shiftCount = genLog2(vectorLength); + int typeSize = genTypeSize(simdBaseType); + int shiftVal = (typeSize * vectorLength) / 2; - if (simdSize == 32) + // The reduced sum is calculated for integer values using a combination of shift + add + // For e.g. consider a 32 bit integer. This means we have 4 values in a XMM register + // After the first shift + add -> [(0 + 2), (1 + 3), ...] + // After the second shift + add -> [(0 + 2 + 1 + 3), ...] + GenTree* opShifted = nullptr; + while (shiftVal >= typeSize) { - intrinsic = (simdBaseType == TYP_FLOAT) ? NI_SSE_Add : NI_SSE2_Add; - - tmp = fgMakeMultiUse(&op1); - op1 = gtNewSimdGetUpperNode(TYP_SIMD16, op1, simdBaseJitType, simdSize); - - tmp = gtNewSimdGetLowerNode(TYP_SIMD16, tmp, simdBaseJitType, simdSize); - op1 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, tmp, intrinsic, simdBaseJitType, 16); + tmp = fgMakeMultiUse(&op1); + opShifted = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, gtNewIconNode(shiftVal, TYP_INT), + NI_SSE2_ShiftRightLogical128BitLane, simdBaseJitType, simdSize); + op1 = gtNewSimdBinOpNode(GT_ADD, TYP_SIMD16, opShifted, tmp, simdBaseJitType, simdSize); + shiftVal = shiftVal / 2; } - return gtNewSimdHWIntrinsicNode(type, op1, NI_Vector128_ToScalar, simdBaseJitType, 16); + return gtNewSimdToScalarNode(type, op1, simdBaseJitType, simdSize); + #elif defined(TARGET_ARM64) switch (simdBaseType) { @@ -24673,6 +24728,52 @@ GenTree* Compiler::gtNewSimdTernaryLogicNode(var_types type, } #endif // TARGET_XARCH +#if defined(TARGET_XARCH) +//---------------------------------------------------------------------------------------------- +// Compiler::gtNewSimdToScalarNode: Creates a new simd ToScalar node. +// +// Arguments: +// type - The return type of SIMD node being created. +// op1 - The SIMD operand. +// simdBaseJitType - The base JIT type of SIMD type of the intrinsic. +// simdSize - The size of the SIMD type of the intrinsic. +// +// Returns: +// The created node that has the ToScalar implementation. +// +GenTree* Compiler::gtNewSimdToScalarNode(var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize) +{ + +#if defined(TARGET_X86) + var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); + if (varTypeIsLong(simdBaseType)) + { + // We need SSE41 to handle long, use software fallback + assert(compIsaSupportedDebugOnly(InstructionSet_SSE41)); + + // Create a GetElement node which handles decomposition + GenTree* op2 = gtNewIconNode(0); + return gtNewSimdGetElementNode(type, op1, op2, simdBaseJitType, simdSize); + } +#endif // TARGET_X86 + // Ensure MOVD/MOVQ support exists + assert(compIsaSupportedDebugOnly(InstructionSet_SSE2)); + NamedIntrinsic intrinsic = NI_Vector128_ToScalar; + + if (simdSize == 32) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + intrinsic = NI_Vector256_ToScalar; + } + else if (simdSize == 64) + { + assert(IsBaselineVector512IsaSupportedDebugOnly()); + intrinsic = NI_Vector512_ToScalar; + } + return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize); +} +#endif // TARGET_XARCH + GenTree* Compiler::gtNewSimdUnOpNode( genTreeOps op, var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize) { diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index bddcb05ad7fce4..8f6ed5f07c0d2a 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -330,6 +330,7 @@ HARDWARE_INTRINSIC(Vector512, StoreAligned, HARDWARE_INTRINSIC(Vector512, StoreAlignedNonTemporal, 64, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, StoreUnsafe, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, Subtract, 64, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) +HARDWARE_INTRINSIC(Vector512, Sum, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromFirstArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(Vector512, ToScalar, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_movsd_simd}, HW_Category_SIMDScalar, HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, WidenLower, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Vector512, WidenUpper, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen|HW_Flag_BaseTypeFromFirstArg) diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index c175496f344c64..97cb490052bb00 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -2877,33 +2877,27 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_Vector128_Sum: case NI_Vector256_Sum: + case NI_Vector512_Sum: { assert(sig->numArgs == 1); var_types simdType = getSIMDTypeForSize(simdSize); if ((simdSize == 32) && !compOpportunisticallyDependsOn(InstructionSet_AVX2)) { - // Vector256 for integer types requires AVX2 + // Vector256 requires AVX2 break; } - else if (varTypeIsFloating(simdBaseType)) + else if ((simdSize == 16) && !compOpportunisticallyDependsOn(InstructionSet_SSE2)) { - if (!compOpportunisticallyDependsOn(InstructionSet_SSE3)) - { - // Floating-point types require SSE3.HorizontalAdd - break; - } - } - else if (!compOpportunisticallyDependsOn(InstructionSet_SSSE3)) - { - // Integral types require SSSE3.HorizontalAdd break; } - else if (varTypeIsByte(simdBaseType) || varTypeIsLong(simdBaseType)) +#if defined(TARGET_X86) + else if (varTypeIsLong(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_SSE41)) { - // byte, sbyte, long, and ulong all would require more work to support + // We need SSE41 to handle long, use software fallback break; } +#endif // TARGET_X86 op1 = impSIMDPopStack(); retNode = gtNewSimdSumNode(retType, op1, simdBaseJitType, simdSize); @@ -2917,23 +2911,15 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, assert(sig->numArgs == 1); #if defined(TARGET_X86) - if (varTypeIsLong(simdBaseType)) + if (varTypeIsLong(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_SSE41)) { - if (!compOpportunisticallyDependsOn(InstructionSet_SSE41)) - { - // We need SSE41 to handle long, use software fallback - break; - } - // Create a GetElement node which handles decomposition - op1 = impSIMDPopStack(); - op2 = gtNewIconNode(0); - retNode = gtNewSimdGetElementNode(retType, op1, op2, simdBaseJitType, simdSize); + // We need SSE41 to handle long, use software fallback break; } #endif // TARGET_X86 op1 = impSIMDPopStack(); - retNode = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize); + retNode = gtNewSimdToScalarNode(retType, op1, simdBaseJitType, simdSize); break; } From 15eb4df61f71e9166f2f43ae58600fa4a7d54311 Mon Sep 17 00:00:00 2001 From: Ivan Diaz Sanchez Date: Wed, 17 Jan 2024 15:21:24 -0800 Subject: [PATCH 086/189] Enable CMake Flag `-W3` as New CMP0092 no Longer Does it Implicitly (#97053) * Add universal `-W3` option to CMakeLists.txt, as the new policy CMP0092 no longer adds it by default, and we use it. * Had not noticed there was already a W3 removal in the compilers' configuration. So no need to add another W3, just remove that removal. --- eng/native/configurecompiler.cmake | 3 --- 1 file changed, 3 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 0f5aaa57820f7c..fdf907a17793e1 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -767,9 +767,6 @@ if (MSVC) # Compile options for targeting windows add_compile_options($<$:/nologo>) # Suppress Startup Banner - # /W3 is added by default by CMake, so remove it - string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - string(REPLACE "/W3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") # [[! Microsoft.Security.SystemsADM.10086 !]] - SDL required warnings # set default warning level to 4 but allow targets to override it. From 3ffa1a969958711d679117478bf9daa8203b8d4d Mon Sep 17 00:00:00 2001 From: Ivan Diaz Sanchez Date: Wed, 17 Jan 2024 15:22:45 -0800 Subject: [PATCH 087/189] Remove Explicit Deletion of CMake `/GR` Flag as 3.20's *CMP0117* by Default Doesn't Add It (#96814) * Remove /GR flag as new CMake Policy doesn't add it by default. * Tell CMake to use the new behavior of CMP0117. * CMake policy is by default set when not specified, so removing the explicit setting in CMakeLists.txt * Flag /GR- fix. * Restored accidentally deleted comment. --- eng/native/configurecompiler.cmake | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index fdf907a17793e1..cc22f2963169f3 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -780,9 +780,7 @@ if (MSVC) add_compile_options($<$:/GS>) # Explicitly enable the buffer security checks add_compile_options($<$:/fp:precise>) # Enable precise floating point - # disable C++ RTTI - # /GR is added by default by CMake, so remove it manually. - string(REPLACE "/GR " " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + # Disable C++ RTTI set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-") add_compile_options($<$:/FC>) # use full pathnames in diagnostics From 08cff56070d835c3c2fb1b3745e75f95d89c44e6 Mon Sep 17 00:00:00 2001 From: SwapnilGaikwad Date: Wed, 17 Jan 2024 23:26:01 +0000 Subject: [PATCH 088/189] Add Arm64 encodings for IF_SVE_CY_3A and IF_SVE_CY_3B group (#96992) * Add Arm64 encodings for IF_SVE_CY_3A and IF_SVE_CY_3B group * Fix function declaration --------- Co-authored-by: Kunal Pathak --- src/coreclr/jit/codegenarm64test.cpp | 24 ++++++ src/coreclr/jit/emitarm64.cpp | 117 ++++++++++++++++++++++++++- src/coreclr/jit/emitarm64.h | 18 +++++ 3 files changed, 155 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 162cf751c37deb..45afd2629a3682 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -4794,6 +4794,30 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_R(INS_sve_cmpne, EA_SCALABLE, REG_P0, REG_P0, REG_V14, REG_V28, INS_OPTS_SCALABLE_B, INS_SCALABLE_OPTS_WIDE); /* CMPNE ., /Z, ., .D */ + // IF_SVE_CY_3A + theEmitter->emitIns_R_R_R_I(INS_sve_cmpeq, EA_SCALABLE, REG_P15, REG_P0, REG_V31, 8, + INS_OPTS_SCALABLE_B); /* CMPEQ ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmpge, EA_SCALABLE, REG_P11, REG_P7, REG_V21, 1, + INS_OPTS_SCALABLE_H); /* CMPGE ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmpgt, EA_SCALABLE, REG_P10, REG_P1, REG_V18, 4, + INS_OPTS_SCALABLE_S); /* CMPGT ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmple, EA_SCALABLE, REG_P8, REG_P6, REG_V11, 15, + INS_OPTS_SCALABLE_D); /* CMPLE ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmplt, EA_SCALABLE, REG_P7, REG_P2, REG_V8, -16, + INS_OPTS_SCALABLE_B); /* CMPLT ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmpne, EA_SCALABLE, REG_P0, REG_P5, REG_V0, -14, + INS_OPTS_SCALABLE_H); /* CMPNE ., /Z, ., # */ + + // IF_SVE_CY_3B + theEmitter->emitIns_R_R_R_I(INS_sve_cmphi, EA_SCALABLE, REG_P15, REG_P7, REG_V19, 0, + INS_OPTS_SCALABLE_B); /* CMPHI ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmphs, EA_SCALABLE, REG_P11, REG_P1, REG_V0, 36, + INS_OPTS_SCALABLE_H); /* CMPHS ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmplo, EA_SCALABLE, REG_P8, REG_P5, REG_V21, 64, + INS_OPTS_SCALABLE_S); /* CMPLO ., /Z, ., # */ + theEmitter->emitIns_R_R_R_I(INS_sve_cmpls, EA_SCALABLE, REG_P0, REG_P3, REG_V9, 127, + INS_OPTS_SCALABLE_D); /* CMPLS ., /Z, ., # */ + // IF_SVE_EP_3A theEmitter->emitIns_R_R_R(INS_sve_shadd, EA_SCALABLE, REG_V15, REG_P0, REG_V10, INS_OPTS_SCALABLE_B); // SHADD ., /M, ., . diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index be18133b3d78c7..ce5a2820e0e43b 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1072,8 +1072,28 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(insOptsScalableWide(id->idInsOpt())); // xx assert(isPredicateRegister(id->idReg1())); // DDDD assert(isLowPredicateRegister(id->idReg2())); // ggg - assert(isVectorRegister(id->idReg3())); // mmmmm - assert(isVectorRegister(id->idReg4())); // nnnnn + assert(isVectorRegister(id->idReg3())); // nnnnn + assert(isVectorRegister(id->idReg4())); // mmmmm + break; + + case IF_SVE_CY_3A: // ........xx.iiiii ...gggnnnnn.DDDD -- SVE integer compare with signed immediate + elemsize = id->idOpSize(); + assert(isScalableVectorSize(elemsize)); + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isPredicateRegister(id->idReg1())); // DDDD + assert(isLowPredicateRegister(id->idReg2())); // ggg + assert(isVectorRegister(id->idReg3())); // nnnnn + assert(isValidSimm5(emitGetInsSC(id))); // iiiii + break; + + case IF_SVE_CY_3B: // ........xx.iiiii ii.gggnnnnn.DDDD -- SVE integer compare with unsigned immediate + elemsize = id->idOpSize(); + assert(isScalableVectorSize(elemsize)); + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isPredicateRegister(id->idReg1())); // DDDD + assert(isLowPredicateRegister(id->idReg2())); // ggg + assert(isVectorRegister(id->idReg3())); // nnnnn + assert(isValidUimm7(emitGetInsSC(id))); // iiiii break; case IF_SVE_GE_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE2 character match @@ -9639,6 +9659,32 @@ void emitter::emitIns_R_R_R_I(instruction ins, } break; + case INS_sve_cmpeq: + case INS_sve_cmpgt: + case INS_sve_cmpge: + case INS_sve_cmpne: + case INS_sve_cmple: + case INS_sve_cmplt: + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isLowPredicateRegister(reg2)); // ggg + assert(isVectorRegister(reg3)); // nnnnn + assert(isValidSimm5(imm)); // iiiii + fmt = IF_SVE_CY_3A; + break; + + case INS_sve_cmphi: + case INS_sve_cmphs: + case INS_sve_cmplo: + case INS_sve_cmpls: + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isLowPredicateRegister(reg2)); // ggg + assert(isVectorRegister(reg3)); // nnnnn + assert(isValidUimm7(imm)); // iiiii + fmt = IF_SVE_CY_3B; + break; + case INS_fmul: // by element, imm[0..3] selects the element of reg3 case INS_fmla: case INS_fmls: @@ -14348,6 +14394,32 @@ void emitter::emitIns_Call(EmitCallType callType, return insEncodeSimm4_19_to_16(imm / 32); } +/***************************************************************************** + * + * Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeSimm5_20_to_16(ssize_t imm) +{ + assert(isValidSimm5(imm)); + if (imm < 0) + { + imm = (imm & 0x1F); + } + return (code_t)imm << 16; +} + +/***************************************************************************** + * + * Returns the encoding for the immediate value as 7-bits at bit locations '20-14'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm7_20_to_14(ssize_t imm) +{ + assert(isValidUimm7(imm)); + return (code_t)imm << 14; +} + /***************************************************************************** * * Returns the encoding to select the 4/8-byte width specifier @@ -16439,6 +16511,28 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_CY_3A: // ........xx.iiiii ...gggnnnnn.DDDD -- SVE integer compare with signed immediate + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeSimm5_20_to_16(imm); // iiiii + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_CY_3B: // ........xx.iiiii ii.gggnnnnn.DDDD -- SVE integer compare with unsigned immediate + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeUimm7_20_to_14(imm); // iiiii + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_GA_2A: // ............iiii ......nnnn.ddddd -- SME2 multi-vec shift narrow imm = emitGetInsSC(id); assert(id->idInsOpt() == INS_OPTS_SCALABLE_H); @@ -19109,8 +19203,17 @@ void emitter::emitDispInsHelp( case IF_SVE_CX_4A_A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE integer compare vectors emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD emitDispPredicateReg(id->idReg2(), PREDICATE_ZERO, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // mmmmm - emitDispSveReg(id->idReg4(), INS_OPTS_SCALABLE_D, false); // nnnnn + emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg4(), INS_OPTS_SCALABLE_D, false); // mmmmm + break; + + // ., /Z, ., # + case IF_SVE_CY_3A: // ........xx.iiiii ...gggnnnnn.DDDD -- SVE integer compare with signed immediate + case IF_SVE_CY_3B: // ........xx.iiiii ii.gggnnnnn.DDDD -- SVE integer compare with unsigned immediate + emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD + emitDispPredicateReg(id->idReg2(), PREDICATE_ZERO, id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn + emitDispImm(emitGetInsSC(id), false, (fmt == IF_SVE_CY_3B)); // iiiii break; // ., /M, . @@ -21692,6 +21795,12 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insThroughput = PERFSCORE_THROUGHPUT_1C; break; + case IF_SVE_CY_3A: // ........xx.iiiii ...gggnnnnn.DDDD -- SVE integer compare with signed immediate + case IF_SVE_CY_3B: // ........xx.iiiii ii.gggnnnnn.DDDD -- SVE integer compare with unsigned immediate + result.insLatency = PERFSCORE_LATENCY_4C; + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + break; + case IF_SVE_GE_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE2 character match case IF_SVE_HT_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE floating-point compare vectors result.insLatency = PERFSCORE_LATENCY_2C; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index ef77505f58d439..e76290a28a28bd 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -518,6 +518,12 @@ static code_t insEncodeSimm4_MultipleOf16_19_to_16(ssize_t imm); // Returns the encoding for the immediate value that is a multiple of 32 as 4-bits at bit locations '19-16'. static code_t insEncodeSimm4_MultipleOf32_19_to_16(ssize_t imm); +// Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. +static code_t insEncodeSimm5_20_to_16(ssize_t imm); + +// Returns the encoding for the immediate value as 7-bits at bit locations '20-14'. +static code_t insEncodeUimm7_20_to_14(ssize_t imm); + // Returns the encoding to select the elemsize for an Arm64 SVE vector instruction plus an immediate. // This specifically encodes the field 'tszh:tszl' at bit locations '23-22:9-8'. static code_t insEncodeSveShift_23_to_22_9_to_0(emitAttr size, bool isRightShift, size_t imm); @@ -580,6 +586,12 @@ static bool isValidUimm5(ssize_t value) return (0 <= value) && (value <= 0x1FLL); }; +// Returns true if 'value' is a legal unsigned immediate 7 bit encoding (such as for CMPLT, CMPNE). +static bool isValidUimm7(ssize_t value) +{ + return (0 <= value) && (value <= 0x7FLL); +}; + // Returns true if 'value' is a legal unsigned immediate 8 bit encoding (such as for FMOV). static bool isValidUimm8(ssize_t value) { @@ -616,6 +628,12 @@ static bool isValidSimm14(ssize_t value) return (-0x2000LL <= value) && (value <= 0x1FFFLL); }; +// Returns true if 'value' is a legal signed immediate 5 bit encoding (such as for CMPLO, CMPHI). +static bool isValidSimm5(ssize_t value) +{ + return (-0x10LL <= value) && (value <= 0xFLL); +}; + // Returns true if 'value' represents a valid 'bitmask immediate' encoding. static bool isValidImmNRS(size_t value, emitAttr size) { From 56769fa054079376d94c4a6b4f5c884f8d95634e Mon Sep 17 00:00:00 2001 From: OwnageIsMagic Date: Thu, 18 Jan 2024 04:11:41 +0300 Subject: [PATCH 089/189] Use corresponding exception message, if null passed (#90505) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use corresponding exception message if null passed * fix * review fixes * fix tests * do not use default message if null passed for SystemException, ApplicationException, IOException * change COMException default message --------- Co-authored-by: David Cantú --- .../src/HostAbortedException.cs | 4 +- .../Hosting/CompositionFailedException.cs | 4 +- .../Mono/ConfigurationErrorsExceptionTest.cs | 5 --- .../src/System/Data/DBConcurrencyException.cs | 4 +- .../src/System/Data/DataException.cs | 44 +++++++++---------- .../System/Data/DBConcurrencyExceptionTest.cs | 11 ++--- .../Protocols/common/DirectoryException.cs | 12 ++--- .../ActiveDirectory/Exception.cs | 20 ++++----- .../src/System/IO/DriveNotFoundException.cs | 4 +- .../IsolatedStorageException.cs | 4 +- .../IO/Packaging/FileFormatException.cs | 8 ++-- .../Net/WebSockets/WebSocketException.cs | 12 ++--- .../src/Resources/Strings.resx | 2 +- .../src/System/AccessViolationException.cs | 4 +- .../src/System/AggregateException.cs | 6 +-- .../src/System/AppDomainUnloadedException.cs | 4 +- .../src/System/ArgumentNullException.cs | 4 +- .../src/System/ArgumentOutOfRangeException.cs | 6 +-- .../src/System/ArithmeticException.cs | 4 +- .../src/System/ArrayTypeMismatchException.cs | 4 +- .../System/CannotUnloadAppDomainException.cs | 4 +- .../Generic/KeyNotFoundException.cs | 4 +- .../src/System/DataMisalignedException.cs | 4 +- .../Tracing/EventSourceException.cs | 4 +- .../Diagnostics/UnreachableException.cs | 4 +- .../src/System/DivideByZeroException.cs | 4 +- .../src/System/DllNotFoundException.cs | 4 +- .../System/DuplicateWaitObjectException.cs | 4 +- .../src/System/EntryPointNotFoundException.cs | 4 +- .../src/System/ExecutionEngineException.cs | 4 +- .../src/System/FieldAccessException.cs | 4 +- .../src/System/FormatException.cs | 4 +- .../Globalization/CultureNotFoundException.cs | 14 +++--- .../System/IO/DirectoryNotFoundException.cs | 4 +- .../src/System/IO/EndOfStreamException.cs | 4 +- .../src/System/IO/InvalidDataException.cs | 4 +- .../src/System/IO/PathTooLongException.cs | 4 +- .../src/System/IndexOutOfRangeException.cs | 4 +- .../InsufficientExecutionStackException.cs | 4 +- .../src/System/InsufficientMemoryException.cs | 19 ++++---- .../src/System/InvalidCastException.cs | 6 +-- .../src/System/InvalidOperationException.cs | 4 +- .../src/System/InvalidProgramException.cs | 4 +- .../src/System/MemberAccessException.cs | 4 +- .../src/System/MethodAccessException.cs | 4 +- .../src/System/MissingFieldException.cs | 4 +- .../src/System/MissingMemberException.cs | 4 +- .../src/System/MissingMethodException.cs | 4 +- .../System/MulticastNotSupportedException.cs | 4 +- .../src/System/NotFiniteNumberException.cs | 8 ++-- .../src/System/NotImplementedException.cs | 4 +- .../src/System/NotSupportedException.cs | 4 +- .../src/System/NullReferenceException.cs | 4 +- .../src/System/ObjectDisposedException.cs | 4 +- .../src/System/OperationCanceledException.cs | 4 +- .../src/System/OutOfMemoryException.cs | 19 ++++---- .../src/System/OverflowException.cs | 4 +- .../System/PlatformNotSupportedException.cs | 4 +- .../src/System/RankException.cs | 4 +- .../Reflection/AmbiguousMatchException.cs | 4 +- .../CustomAttributeFormatException.cs | 2 +- .../InvalidFilterCriteriaException.cs | 2 +- .../Reflection/TargetInvocationException.cs | 2 +- .../TargetParameterCountException.cs | 4 +- .../MissingManifestResourceException.cs | 4 +- .../MissingSatelliteAssemblyException.cs | 6 +-- .../AmbiguousImplementationException.cs | 4 +- .../SwitchExpressionException.cs | 4 +- .../Runtime/InteropServices/COMException.cs | 6 +-- .../InteropServices/ExternalException.cs | 6 +-- .../InvalidComObjectException.cs | 4 +- .../InvalidOleVariantTypeException.cs | 4 +- .../MarshalDirectiveException.cs | 4 +- .../SafeArrayRankMismatchException.cs | 4 +- .../SafeArrayTypeMismatchException.cs | 4 +- .../Serialization/SerializationException.cs | 4 +- .../System/Security/CryptographicException.cs | 4 +- .../src/System/Security/SecurityException.cs | 8 ++-- .../System/Security/VerificationException.cs | 4 +- .../src/System/StackOverflowException.cs | 4 +- .../Threading/AbandonedMutexException.cs | 8 ++-- .../Threading/SemaphoreFullException.cs | 4 +- .../Threading/SynchronizationLockException.cs | 4 +- .../Threading/Tasks/TaskCanceledException.cs | 6 +-- .../Threading/Tasks/TaskSchedulerException.cs | 4 +- .../Threading/ThreadInterruptedException.cs | 19 ++++---- .../System/Threading/ThreadStateException.cs | 4 +- .../WaitHandleCannotBeOpenedException.cs | 4 +- .../src/System/TimeoutException.cs | 4 +- .../src/System/TypeAccessException.cs | 4 +- .../src/System/TypeInitializationException.cs | 4 +- .../src/System/TypeUnloadedException.cs | 4 +- .../src/System/UnauthorizedAccessException.cs | 4 +- .../AuthenticationTagMismatchException.cs | 4 +- ...yptographicUnexpectedOperationException.cs | 4 +- .../Principal/IdentityNotMappedException.cs | 4 +- .../RegexMatchTimeoutException.cs | 6 +-- .../Channels/ChannelClosedException.cs | 4 +- .../Transactions/TransactionException.cs | 16 +++---- 99 files changed, 281 insertions(+), 286 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/HostAbortedException.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/HostAbortedException.cs index bb5f8b675a08dc..412c00ae19fd90 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/HostAbortedException.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/HostAbortedException.cs @@ -34,7 +34,7 @@ public HostAbortedException() : base(SR.HostAbortedExceptionMessage) { } /// The caller of this constructor is required to ensure that this string has been localized for the /// current system culture. /// - public HostAbortedException(string? message) : base(message) { } + public HostAbortedException(string? message) : base(message ?? SR.HostAbortedExceptionMessage) { } /// /// Initializes a new instance of the class @@ -52,6 +52,6 @@ public HostAbortedException(string? message) : base(message) { } /// The caller of this constructor is required to ensure that this string has been localized for the /// current system culture. /// - public HostAbortedException(string? message, Exception? innerException) : base(message, innerException) { } + public HostAbortedException(string? message, Exception? innerException) : base(message ?? SR.HostAbortedExceptionMessage, innerException) { } } } diff --git a/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/CompositionFailedException.cs b/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/CompositionFailedException.cs index e461bca158288d..a36935db790e69 100644 --- a/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/CompositionFailedException.cs +++ b/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/CompositionFailedException.cs @@ -22,7 +22,7 @@ public CompositionFailedException() /// /// The exception message. public CompositionFailedException(string message) - : base(message) + : base(message ?? SR.CompositionFailedDefaultExceptionMessage) { } /// @@ -31,7 +31,7 @@ public CompositionFailedException(string message) /// The exception message. /// The inner exception. public CompositionFailedException(string message, Exception innerException) - : base(message, innerException) + : base(message ?? SR.CompositionFailedDefaultExceptionMessage, innerException) { } } } diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationErrorsExceptionTest.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationErrorsExceptionTest.cs index 147265c36adb74..e6a7e7e8ceead1 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationErrorsExceptionTest.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationErrorsExceptionTest.cs @@ -46,11 +46,6 @@ public void Constructor1() ConfigurationErrorsException cee = new ConfigurationErrorsException(); Assert.NotNull(cee.BareMessage); - // \p{Pi} any kind of opening quote https://www.compart.com/en/unicode/category/Pi - // \p{Pf} any kind of closing quote https://www.compart.com/en/unicode/category/Pf - // \p{Po} any kind of punctuation character that is not a dash, bracket, quote or connector https://www.compart.com/en/unicode/category/Po - Assert.Matches(@"[\p{Pi}\p{Po}]" + Regex.Escape(typeof(ConfigurationErrorsException).FullName) + @"[\p{Pf}\p{Po}]", cee.BareMessage); - Assert.NotNull(cee.Data); Assert.Equal(0, cee.Data.Count); Assert.Null(cee.Filename); diff --git a/src/libraries/System.Data.Common/src/System/Data/DBConcurrencyException.cs b/src/libraries/System.Data.Common/src/System/Data/DBConcurrencyException.cs index e99408d9567903..855e2b4c0abdcc 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DBConcurrencyException.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DBConcurrencyException.cs @@ -21,12 +21,12 @@ public DBConcurrencyException(string? message) : this(message, null) { } - public DBConcurrencyException(string? message, Exception? inner) : base(message, inner) + public DBConcurrencyException(string? message, Exception? inner) : base(message ?? SR.ADP_DBConcurrencyExceptionMessage, inner) { HResult = HResults.DBConcurrency; } - public DBConcurrencyException(string? message, Exception? inner, DataRow[]? dataRows) : base(message, inner) + public DBConcurrencyException(string? message, Exception? inner, DataRow[]? dataRows) : base(message ?? SR.ADP_DBConcurrencyExceptionMessage, inner) { HResult = HResults.DBConcurrency; _dataRows = dataRows; diff --git a/src/libraries/System.Data.Common/src/System/Data/DataException.cs b/src/libraries/System.Data.Common/src/System/Data/DataException.cs index 556dcba9ae9feb..a63ca2b7721a0c 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataException.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataException.cs @@ -23,12 +23,12 @@ public DataException() : base(SR.DataSet_DefaultDataException) HResult = HResults.Data; } - public DataException(string? s) : base(s) + public DataException(string? s) : base(s ?? SR.DataSet_DefaultDataException) { HResult = HResults.Data; } - public DataException(string? s, Exception? innerException) : base(s, innerException) { } + public DataException(string? s, Exception? innerException) : base(s ?? SR.DataSet_DefaultDataException, innerException) { } }; [Serializable] @@ -46,12 +46,12 @@ public ConstraintException() : base(SR.DataSet_DefaultConstraintException) HResult = HResults.DataConstraint; } - public ConstraintException(string? s) : base(s) + public ConstraintException(string? s) : base(s ?? SR.DataSet_DefaultConstraintException) { HResult = HResults.DataConstraint; } - public ConstraintException(string? message, Exception? innerException) : base(message, innerException) + public ConstraintException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultConstraintException, innerException) { HResult = HResults.DataConstraint; } @@ -78,12 +78,12 @@ public DeletedRowInaccessibleException() : base(SR.DataSet_DefaultDeletedRowInac /// /// Initializes a new instance of the class with the specified string. /// - public DeletedRowInaccessibleException(string? s) : base(s) + public DeletedRowInaccessibleException(string? s) : base(s ?? SR.DataSet_DefaultDeletedRowInaccessibleException) { HResult = HResults.DataDeletedRowInaccessible; } - public DeletedRowInaccessibleException(string? message, Exception? innerException) : base(message, innerException) + public DeletedRowInaccessibleException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultDeletedRowInaccessibleException, innerException) { HResult = HResults.DataDeletedRowInaccessible; } @@ -104,12 +104,12 @@ public DuplicateNameException() : base(SR.DataSet_DefaultDuplicateNameException) HResult = HResults.DataDuplicateName; } - public DuplicateNameException(string? s) : base(s) + public DuplicateNameException(string? s) : base(s ?? SR.DataSet_DefaultDuplicateNameException) { HResult = HResults.DataDuplicateName; } - public DuplicateNameException(string? message, Exception? innerException) : base(message, innerException) + public DuplicateNameException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultDuplicateNameException, innerException) { HResult = HResults.DataDuplicateName; } @@ -130,12 +130,12 @@ public InRowChangingEventException() : base(SR.DataSet_DefaultInRowChangingEvent HResult = HResults.DataInRowChangingEvent; } - public InRowChangingEventException(string? s) : base(s) + public InRowChangingEventException(string? s) : base(s ?? SR.DataSet_DefaultInRowChangingEventException) { HResult = HResults.DataInRowChangingEvent; } - public InRowChangingEventException(string? message, Exception? innerException) : base(message, innerException) + public InRowChangingEventException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultInRowChangingEventException, innerException) { HResult = HResults.DataInRowChangingEvent; } @@ -156,12 +156,12 @@ public InvalidConstraintException() : base(SR.DataSet_DefaultInvalidConstraintEx HResult = HResults.DataInvalidConstraint; } - public InvalidConstraintException(string? s) : base(s) + public InvalidConstraintException(string? s) : base(s ?? SR.DataSet_DefaultInvalidConstraintException) { HResult = HResults.DataInvalidConstraint; } - public InvalidConstraintException(string? message, Exception? innerException) : base(message, innerException) + public InvalidConstraintException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultInvalidConstraintException, innerException) { HResult = HResults.DataInvalidConstraint; } @@ -182,12 +182,12 @@ public MissingPrimaryKeyException() : base(SR.DataSet_DefaultMissingPrimaryKeyEx HResult = HResults.DataMissingPrimaryKey; } - public MissingPrimaryKeyException(string? s) : base(s) + public MissingPrimaryKeyException(string? s) : base(s ?? SR.DataSet_DefaultMissingPrimaryKeyException) { HResult = HResults.DataMissingPrimaryKey; } - public MissingPrimaryKeyException(string? message, Exception? innerException) : base(message, innerException) + public MissingPrimaryKeyException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultMissingPrimaryKeyException, innerException) { HResult = HResults.DataMissingPrimaryKey; } @@ -208,12 +208,12 @@ public NoNullAllowedException() : base(SR.DataSet_DefaultNoNullAllowedException) HResult = HResults.DataNoNullAllowed; } - public NoNullAllowedException(string? s) : base(s) + public NoNullAllowedException(string? s) : base(s ?? SR.DataSet_DefaultNoNullAllowedException) { HResult = HResults.DataNoNullAllowed; } - public NoNullAllowedException(string? message, Exception? innerException) : base(message, innerException) + public NoNullAllowedException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultNoNullAllowedException, innerException) { HResult = HResults.DataNoNullAllowed; } @@ -234,12 +234,12 @@ public ReadOnlyException() : base(SR.DataSet_DefaultReadOnlyException) HResult = HResults.DataReadOnly; } - public ReadOnlyException(string? s) : base(s) + public ReadOnlyException(string? s) : base(s ?? SR.DataSet_DefaultReadOnlyException) { HResult = HResults.DataReadOnly; } - public ReadOnlyException(string? message, Exception? innerException) : base(message, innerException) + public ReadOnlyException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultReadOnlyException, innerException) { HResult = HResults.DataReadOnly; } @@ -260,12 +260,12 @@ public RowNotInTableException() : base(SR.DataSet_DefaultRowNotInTableException) HResult = HResults.DataRowNotInTable; } - public RowNotInTableException(string? s) : base(s) + public RowNotInTableException(string? s) : base(s ?? SR.DataSet_DefaultRowNotInTableException) { HResult = HResults.DataRowNotInTable; } - public RowNotInTableException(string? message, Exception? innerException) : base(message, innerException) + public RowNotInTableException(string? message, Exception? innerException) : base(message ?? SR.DataSet_DefaultRowNotInTableException, innerException) { HResult = HResults.DataRowNotInTable; } @@ -286,12 +286,12 @@ public VersionNotFoundException() : base(SR.DataSet_DefaultVersionNotFoundExcept HResult = HResults.DataVersionNotFound; } - public VersionNotFoundException(string? s) : base(s) + public VersionNotFoundException(string? s) : base(s ?? (SR.DataSet_DefaultVersionNotFoundException)) { HResult = HResults.DataVersionNotFound; } - public VersionNotFoundException(string? message, Exception? innerException) : base(message, innerException) + public VersionNotFoundException(string? message, Exception? innerException) : base(message ?? (SR.DataSet_DefaultVersionNotFoundException), innerException) { HResult = HResults.DataVersionNotFound; } diff --git a/src/libraries/System.Data.Common/tests/System/Data/DBConcurrencyExceptionTest.cs b/src/libraries/System.Data.Common/tests/System/Data/DBConcurrencyExceptionTest.cs index 47df1abddc6b1a..5ea12f0354d4cb 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/DBConcurrencyExceptionTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/DBConcurrencyExceptionTest.cs @@ -35,7 +35,6 @@ public void Constructor1() DBConcurrencyException dbce = new DBConcurrencyException(); Assert.Null(dbce.InnerException); Assert.NotNull(dbce.Message); - Assert.NotNull(dbce.Message); Assert.Null(dbce.Row); Assert.Equal(0, dbce.RowCount); } @@ -55,9 +54,7 @@ public void Constructor2() dbce = new DBConcurrencyException(null); Assert.Null(dbce.InnerException); Assert.NotNull(dbce.Message); - Assert.Contains(typeof(DBConcurrencyException).FullName, dbce.Message); Assert.Null(dbce.Row); - Assert.Equal(0, dbce.RowCount); dbce = new DBConcurrencyException(string.Empty); @@ -82,7 +79,7 @@ public void Constructor3() dbce = new DBConcurrencyException(null, inner); Assert.Same(inner, dbce.InnerException); - Assert.Contains(typeof(DBConcurrencyException).FullName, dbce.Message); + Assert.NotNull(dbce.Message); Assert.Null(dbce.Row); Assert.Equal(0, dbce.RowCount); @@ -100,7 +97,7 @@ public void Constructor3() dbce = new DBConcurrencyException(null, null); Assert.Null(dbce.InnerException); - Assert.Contains(typeof(DBConcurrencyException).FullName, dbce.Message); + Assert.NotNull(dbce.Message); Assert.Null(dbce.Row); Assert.Equal(0, dbce.RowCount); } @@ -126,7 +123,7 @@ public void Constructor4() rows = new DataRow[] { rowB, rowA, null }; dbce = new DBConcurrencyException(null, inner, rows); Assert.Same(inner, dbce.InnerException); - Assert.Contains(typeof(DBConcurrencyException).FullName, dbce.Message); + Assert.NotNull(dbce.Message); Assert.Same(rowB, dbce.Row); Assert.Equal(3, dbce.RowCount); @@ -154,7 +151,7 @@ public void Constructor4() rows = null; dbce = new DBConcurrencyException(null, null, rows); Assert.Null(dbce.InnerException); - Assert.Contains(typeof(DBConcurrencyException).FullName, dbce.Message); + Assert.NotNull(dbce.Message); Assert.Null(dbce.Row); Assert.Equal(0, dbce.RowCount); } diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs index b4134d58e9b3ed..48235f0cbc1ef5 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryException.cs @@ -42,11 +42,11 @@ public class DirectoryOperationException : DirectoryException, ISerializable #endif protected DirectoryOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { } - public DirectoryOperationException() : base() { } + public DirectoryOperationException() : base(SR.DefaultOperationsError) { } - public DirectoryOperationException(string message) : base(message) { } + public DirectoryOperationException(string message) : base(message ?? SR.DefaultOperationsError) { } - public DirectoryOperationException(string message, Exception inner) : base(message, inner) { } + public DirectoryOperationException(string message, Exception inner) : base(message ?? SR.DefaultOperationsError, inner) { } public DirectoryOperationException(DirectoryResponse response) : base(CreateMessage(response, message: null)) @@ -68,7 +68,7 @@ public DirectoryOperationException(DirectoryResponse response, string message, E public DirectoryResponse Response { get; internal set; } - private static string CreateMessage(DirectoryResponse response, string message) + private static string CreateMessage(DirectoryResponse response, string? message) { string result = message ?? SR.DefaultOperationsError; if (!string.IsNullOrEmpty(response?.ErrorMessage)) @@ -93,11 +93,11 @@ public BerConversionException() : base(SR.BerConversionError) { } - public BerConversionException(string message) : base(message) + public BerConversionException(string message) : base(message ?? SR.BerConversionError) { } - public BerConversionException(string message, Exception inner) : base(message, inner) + public BerConversionException(string message, Exception inner) : base(message ?? SR.BerConversionError, inner) { } } diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs index b5887f12cc2758..85ea36b623f06a 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs @@ -87,19 +87,19 @@ public override string Message [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public class ActiveDirectoryOperationException : Exception, ISerializable { - public ActiveDirectoryOperationException(string? message, Exception? inner, int errorCode) : base(message, inner) + public ActiveDirectoryOperationException(string? message, Exception? inner, int errorCode) : base(message ?? SR.DSUnknownFailure, inner) { ErrorCode = errorCode; } - public ActiveDirectoryOperationException(string? message, int errorCode) : base(message) + public ActiveDirectoryOperationException(string? message, int errorCode) : base(message ?? SR.DSUnknownFailure) { ErrorCode = errorCode; } - public ActiveDirectoryOperationException(string? message, Exception? inner) : base(message, inner) { } + public ActiveDirectoryOperationException(string? message, Exception? inner) : base(message ?? SR.DSUnknownFailure, inner) { } - public ActiveDirectoryOperationException(string? message) : base(message) { } + public ActiveDirectoryOperationException(string? message) : base(message ?? SR.DSUnknownFailure) { } public ActiveDirectoryOperationException() : base(SR.DSUnknownFailure) { } @@ -205,14 +205,14 @@ public class SyncFromAllServersOperationException : ActiveDirectoryOperationExce { private readonly SyncFromAllServersErrorInformation[]? _errors; - public SyncFromAllServersOperationException(string? message, Exception? inner, SyncFromAllServersErrorInformation[]? errors) : base(message, inner) + public SyncFromAllServersOperationException(string? message, Exception? inner, SyncFromAllServersErrorInformation[]? errors) : base(message ?? SR.DSSyncAllFailure, inner) { _errors = errors; } - public SyncFromAllServersOperationException(string? message, Exception? inner) : base(message, inner) { } + public SyncFromAllServersOperationException(string? message, Exception? inner) : base(message ?? SR.DSSyncAllFailure, inner) { } - public SyncFromAllServersOperationException(string? message) : base(message) { } + public SyncFromAllServersOperationException(string? message) : base(message ?? SR.DSSyncAllFailure) { } public SyncFromAllServersOperationException() : base(SR.DSSyncAllFailure) { } @@ -253,14 +253,14 @@ public override void GetObjectData(SerializationInfo serializationInfo, Streamin [System.Runtime.CompilerServices.TypeForwardedFrom("System.DirectoryServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public class ForestTrustCollisionException : ActiveDirectoryOperationException, ISerializable { - public ForestTrustCollisionException(string? message, Exception? inner, ForestTrustRelationshipCollisionCollection? collisions) : base(message, inner) + public ForestTrustCollisionException(string? message, Exception? inner, ForestTrustRelationshipCollisionCollection? collisions) : base(message ?? SR.ForestTrustCollision, inner) { Collisions = collisions; } - public ForestTrustCollisionException(string? message, Exception? inner) : base(message, inner) { } + public ForestTrustCollisionException(string? message, Exception? inner) : base(message ?? SR.ForestTrustCollision, inner) { } - public ForestTrustCollisionException(string? message) : base(message) { } + public ForestTrustCollisionException(string? message) : base(message ?? SR.ForestTrustCollision) { } public ForestTrustCollisionException() : base(SR.ForestTrustCollision) { } diff --git a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs index 1d0873a2ba8c3e..42c3ee3126d2d2 100644 --- a/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs +++ b/src/libraries/System.IO.FileSystem.DriveInfo/src/System/IO/DriveNotFoundException.cs @@ -18,13 +18,13 @@ public DriveNotFoundException() } public DriveNotFoundException(string? message) - : base(message) + : base(message ?? SR.IO_DriveNotFound) { HResult = HResults.COR_E_DIRECTORYNOTFOUND; } public DriveNotFoundException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.IO_DriveNotFound, innerException) { HResult = HResults.COR_E_DIRECTORYNOTFOUND; } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs index 7c42d59b68e034..f0814113910165 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageException.cs @@ -24,13 +24,13 @@ public IsolatedStorageException() } public IsolatedStorageException(string? message) - : base(message) + : base(message ?? SR.IsolatedStorage_Exception) { HResult = COR_E_ISOSTORE; } public IsolatedStorageException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.IsolatedStorage_Exception, inner) { HResult = COR_E_ISOSTORE; } diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs index 6bed53f5796374..9946c94badff4d 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/FileFormatException.cs @@ -30,7 +30,7 @@ public FileFormatException() /// /// The message that describes the error. public FileFormatException(string? message) - : base(message) + : base(message ?? SR.FileFormatException) { } /// @@ -41,7 +41,7 @@ public FileFormatException(string? message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. public FileFormatException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.FileFormatException, innerException) { } /// @@ -71,7 +71,7 @@ public FileFormatException(Uri? sourceUri) /// The Uri of a file that caused this error. /// The message that describes the error. public FileFormatException(Uri? sourceUri, string? message) - : base(message) + : base(message ?? SR.FileFormatException) { _sourceUri = sourceUri; } @@ -108,7 +108,7 @@ public FileFormatException(Uri? sourceUri, Exception? innerException) /// The message that describes the error. /// The exception that is the cause of the current exception. public FileFormatException(Uri? sourceUri, string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.FileFormatException, innerException) { _sourceUri = sourceUri; } diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs index 4f4c5d5468be14..ccc80dae98f0ac 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketException.cs @@ -24,7 +24,7 @@ public WebSocketException(WebSocketError error) { } - public WebSocketException(WebSocketError error, string? message) : base(message) + public WebSocketException(WebSocketError error, string? message) : base(message ?? SR.net_WebSockets_Generic) { _webSocketErrorCode = error; } @@ -35,7 +35,7 @@ public WebSocketException(WebSocketError error, Exception? innerException) } public WebSocketException(WebSocketError error, string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.net_WebSockets_Generic, innerException) { _webSocketErrorCode = error; } @@ -67,7 +67,7 @@ public WebSocketException(WebSocketError error, int nativeError) } public WebSocketException(WebSocketError error, int nativeError, string? message) - : base(message) + : base(message ?? SR.net_WebSockets_Generic) { _webSocketErrorCode = error; SetErrorCodeOnError(nativeError); @@ -79,19 +79,19 @@ public WebSocketException(WebSocketError error, int nativeError, Exception? inne } public WebSocketException(WebSocketError error, int nativeError, string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.net_WebSockets_Generic, innerException) { _webSocketErrorCode = error; SetErrorCodeOnError(nativeError); } public WebSocketException(string? message) - : base(message) + : base(message ?? SR.net_WebSockets_Generic) { } public WebSocketException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.net_WebSockets_Generic, innerException) { } diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 53cc0c13d1abb6..68969b6453939b 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -257,7 +257,7 @@ Must specify property Set or Get or method call for a COM Object. - Error HRESULT E_FAIL has been returned from a call to a COM component. + Unexpected HRESULT has been returned from a call to a COM component. Only one of the following binding flags can be set: BindingFlags.SetProperty, BindingFlags.PutDispProperty, BindingFlags.PutRefDispProperty. diff --git a/src/libraries/System.Private.CoreLib/src/System/AccessViolationException.cs b/src/libraries/System.Private.CoreLib/src/System/AccessViolationException.cs index 32b5064d0ddef1..80c7db151b13aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AccessViolationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AccessViolationException.cs @@ -21,13 +21,13 @@ public AccessViolationException() } public AccessViolationException(string? message) - : base(message) + : base(message ?? SR.Arg_AccessViolationException) { HResult = HResults.E_POINTER; } public AccessViolationException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_AccessViolationException, innerException) { HResult = HResults.E_POINTER; } diff --git a/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs b/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs index 355b0b66204d91..f9ec9353812d4c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs @@ -40,7 +40,7 @@ public AggregateException() /// /// The error message that explains the reason for the exception. public AggregateException(string? message) - : base(message) + : base(message ?? SR.AggregateException_ctor_DefaultMessage) { _innerExceptions = Array.Empty(); } @@ -54,7 +54,7 @@ public AggregateException(string? message) /// The argument /// is null. public AggregateException(string? message, Exception innerException) - : base(message, innerException) + : base(message ?? SR.AggregateException_ctor_DefaultMessage, innerException) { ArgumentNullException.ThrowIfNull(innerException); @@ -120,7 +120,7 @@ public AggregateException(string? message, params Exception[] innerExceptions) : } private AggregateException(string? message, Exception[] innerExceptions, bool cloneExceptions) : - base(message, innerExceptions.Length > 0 ? innerExceptions[0] : null) + base(message ?? SR.AggregateException_ctor_DefaultMessage, innerExceptions.Length > 0 ? innerExceptions[0] : null) { _innerExceptions = cloneExceptions ? new Exception[innerExceptions.Length] : innerExceptions; diff --git a/src/libraries/System.Private.CoreLib/src/System/AppDomainUnloadedException.cs b/src/libraries/System.Private.CoreLib/src/System/AppDomainUnloadedException.cs index 9b00bce65beb21..321dd47be488e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppDomainUnloadedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppDomainUnloadedException.cs @@ -18,13 +18,13 @@ public AppDomainUnloadedException() } public AppDomainUnloadedException(string? message) - : base(message) + : base(message ?? SR.Arg_AppDomainUnloadedException) { HResult = HResults.COR_E_APPDOMAINUNLOADED; } public AppDomainUnloadedException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_AppDomainUnloadedException, innerException) { HResult = HResults.COR_E_APPDOMAINUNLOADED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs b/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs index 4bbf0ca545521b..b563c12dfa3567 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs @@ -31,13 +31,13 @@ public ArgumentNullException(string? paramName) } public ArgumentNullException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.ArgumentNull_Generic, innerException) { HResult = HResults.E_POINTER; } public ArgumentNullException(string? paramName, string? message) - : base(message, paramName) + : base(message ?? SR.ArgumentNull_Generic, paramName) { HResult = HResults.E_POINTER; } diff --git a/src/libraries/System.Private.CoreLib/src/System/ArgumentOutOfRangeException.cs b/src/libraries/System.Private.CoreLib/src/System/ArgumentOutOfRangeException.cs index b64df585efbfbd..9dc65537a66dbb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ArgumentOutOfRangeException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ArgumentOutOfRangeException.cs @@ -34,19 +34,19 @@ public ArgumentOutOfRangeException(string? paramName) } public ArgumentOutOfRangeException(string? paramName, string? message) - : base(message, paramName) + : base(message ?? SR.Arg_ArgumentOutOfRangeException, paramName) { HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_ArgumentOutOfRangeException, innerException) { HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; } public ArgumentOutOfRangeException(string? paramName, object? actualValue, string? message) - : base(message, paramName) + : base(message ?? SR.Arg_ArgumentOutOfRangeException, paramName) { _actualValue = actualValue; HResult = HResults.COR_E_ARGUMENTOUTOFRANGE; diff --git a/src/libraries/System.Private.CoreLib/src/System/ArithmeticException.cs b/src/libraries/System.Private.CoreLib/src/System/ArithmeticException.cs index 510a0c412ea105..093222d496c1b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ArithmeticException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ArithmeticException.cs @@ -28,13 +28,13 @@ public ArithmeticException() // and its ExceptionInfo reference set to null. // public ArithmeticException(string? message) - : base(message) + : base(message ?? SR.Arg_ArithmeticException) { HResult = HResults.COR_E_ARITHMETIC; } public ArithmeticException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_ArithmeticException, innerException) { HResult = HResults.COR_E_ARITHMETIC; } diff --git a/src/libraries/System.Private.CoreLib/src/System/ArrayTypeMismatchException.cs b/src/libraries/System.Private.CoreLib/src/System/ArrayTypeMismatchException.cs index f06701be792066..d1e5f96358ba52 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ArrayTypeMismatchException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ArrayTypeMismatchException.cs @@ -28,13 +28,13 @@ public ArrayTypeMismatchException() // and its ExceptionInfo reference set to null. // public ArrayTypeMismatchException(string? message) - : base(message) + : base(message ?? SR.Arg_ArrayTypeMismatchException) { HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } public ArrayTypeMismatchException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_ArrayTypeMismatchException, innerException) { HResult = HResults.COR_E_ARRAYTYPEMISMATCH; } diff --git a/src/libraries/System.Private.CoreLib/src/System/CannotUnloadAppDomainException.cs b/src/libraries/System.Private.CoreLib/src/System/CannotUnloadAppDomainException.cs index d23f947639f4d2..0f915e1ff1e6e1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/CannotUnloadAppDomainException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/CannotUnloadAppDomainException.cs @@ -18,13 +18,13 @@ public CannotUnloadAppDomainException() } public CannotUnloadAppDomainException(string? message) - : base(message) + : base(message ?? SR.Arg_CannotUnloadAppDomainException) { HResult = HResults.COR_E_CANNOTUNLOADAPPDOMAIN; } public CannotUnloadAppDomainException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_CannotUnloadAppDomainException, innerException) { HResult = HResults.COR_E_CANNOTUNLOADAPPDOMAIN; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyNotFoundException.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyNotFoundException.cs index f0d34db23b95df..2afb92204427d5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyNotFoundException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyNotFoundException.cs @@ -18,13 +18,13 @@ public KeyNotFoundException() } public KeyNotFoundException(string? message) - : base(message) + : base(message ?? SR.Arg_KeyNotFound) { HResult = HResults.COR_E_KEYNOTFOUND; } public KeyNotFoundException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_KeyNotFound, innerException) { HResult = HResults.COR_E_KEYNOTFOUND; } diff --git a/src/libraries/System.Private.CoreLib/src/System/DataMisalignedException.cs b/src/libraries/System.Private.CoreLib/src/System/DataMisalignedException.cs index 7990c153b8c55f..d14fa88d1ab934 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DataMisalignedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DataMisalignedException.cs @@ -21,13 +21,13 @@ public DataMisalignedException() } public DataMisalignedException(string? message) - : base(message) + : base(message ?? SR.Arg_DataMisalignedException) { HResult = HResults.COR_E_DATAMISALIGNED; } public DataMisalignedException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_DataMisalignedException, innerException) { HResult = HResults.COR_E_DATAMISALIGNED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSourceException.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSourceException.cs index 29a2a53c927466..2bdacfa3a14ba4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSourceException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSourceException.cs @@ -22,13 +22,13 @@ public EventSourceException() : /// /// Initializes a new instance of the EventSourceException class with a specified error message. /// - public EventSourceException(string? message) : base(message) { } + public EventSourceException(string? message) : base(message ?? SR.EventSource_ListenerWriteFailure) { } /// /// Initializes a new instance of the EventSourceException class with a specified error message /// and a reference to the inner exception that is the cause of this exception. /// - public EventSourceException(string? message, Exception? innerException) : base(message, innerException) { } + public EventSourceException(string? message, Exception? innerException) : base(message ?? SR.EventSource_ListenerWriteFailure, innerException) { } /// /// Initializes a new instance of the EventSourceException class with serialized data. diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/UnreachableException.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/UnreachableException.cs index 1b1a22b50995fb..c2f94cc2fde025 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/UnreachableException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/UnreachableException.cs @@ -22,7 +22,7 @@ public UnreachableException() /// /// The error message that explains the reason for the exception. public UnreachableException(string? message) - : base(message) + : base(message ?? SR.Arg_UnreachableException) { } @@ -34,7 +34,7 @@ public UnreachableException(string? message) /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. public UnreachableException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_UnreachableException, innerException) { } } diff --git a/src/libraries/System.Private.CoreLib/src/System/DivideByZeroException.cs b/src/libraries/System.Private.CoreLib/src/System/DivideByZeroException.cs index 33b2853620ba99..350552182dfd6a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DivideByZeroException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DivideByZeroException.cs @@ -21,13 +21,13 @@ public DivideByZeroException() } public DivideByZeroException(string? message) - : base(message) + : base(message ?? SR.Arg_DivideByZero) { HResult = HResults.COR_E_DIVIDEBYZERO; } public DivideByZeroException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_DivideByZero, innerException) { HResult = HResults.COR_E_DIVIDEBYZERO; } diff --git a/src/libraries/System.Private.CoreLib/src/System/DllNotFoundException.cs b/src/libraries/System.Private.CoreLib/src/System/DllNotFoundException.cs index 76e6968e48a584..b0cb6936e6a96c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DllNotFoundException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DllNotFoundException.cs @@ -21,13 +21,13 @@ public DllNotFoundException() } public DllNotFoundException(string? message) - : base(message) + : base(message ?? SR.Arg_DllNotFoundException) { HResult = HResults.COR_E_DLLNOTFOUND; } public DllNotFoundException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_DllNotFoundException, inner) { HResult = HResults.COR_E_DLLNOTFOUND; } diff --git a/src/libraries/System.Private.CoreLib/src/System/DuplicateWaitObjectException.cs b/src/libraries/System.Private.CoreLib/src/System/DuplicateWaitObjectException.cs index f83bde1bdd2a3f..aa414d1002f1b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DuplicateWaitObjectException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DuplicateWaitObjectException.cs @@ -29,13 +29,13 @@ public DuplicateWaitObjectException(string? parameterName) } public DuplicateWaitObjectException(string? parameterName, string? message) - : base(message, parameterName) + : base(message ?? SR.Arg_DuplicateWaitObjectException, parameterName) { HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } public DuplicateWaitObjectException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_DuplicateWaitObjectException, innerException) { HResult = HResults.COR_E_DUPLICATEWAITOBJECT; } diff --git a/src/libraries/System.Private.CoreLib/src/System/EntryPointNotFoundException.cs b/src/libraries/System.Private.CoreLib/src/System/EntryPointNotFoundException.cs index 4eac8c07d538d8..a0aa9b12089699 100644 --- a/src/libraries/System.Private.CoreLib/src/System/EntryPointNotFoundException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/EntryPointNotFoundException.cs @@ -21,13 +21,13 @@ public EntryPointNotFoundException() } public EntryPointNotFoundException(string? message) - : base(message) + : base(message ?? SR.Arg_EntryPointNotFoundException) { HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } public EntryPointNotFoundException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_EntryPointNotFoundException, inner) { HResult = HResults.COR_E_ENTRYPOINTNOTFOUND; } diff --git a/src/libraries/System.Private.CoreLib/src/System/ExecutionEngineException.cs b/src/libraries/System.Private.CoreLib/src/System/ExecutionEngineException.cs index 1cdb62dc705fec..283a70a98c9e5d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ExecutionEngineException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ExecutionEngineException.cs @@ -21,13 +21,13 @@ public ExecutionEngineException() } public ExecutionEngineException(string? message) - : base(message) + : base(message ?? SR.Arg_ExecutionEngineException) { HResult = HResults.COR_E_EXECUTIONENGINE; } public ExecutionEngineException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_ExecutionEngineException, innerException) { HResult = HResults.COR_E_EXECUTIONENGINE; } diff --git a/src/libraries/System.Private.CoreLib/src/System/FieldAccessException.cs b/src/libraries/System.Private.CoreLib/src/System/FieldAccessException.cs index 5855b9e6c5ebcc..029f65c70a5345 100644 --- a/src/libraries/System.Private.CoreLib/src/System/FieldAccessException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/FieldAccessException.cs @@ -21,13 +21,13 @@ public FieldAccessException() } public FieldAccessException(string? message) - : base(message) + : base(message ?? SR.Arg_FieldAccessException) { HResult = HResults.COR_E_FIELDACCESS; } public FieldAccessException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_FieldAccessException, inner) { HResult = HResults.COR_E_FIELDACCESS; } diff --git a/src/libraries/System.Private.CoreLib/src/System/FormatException.cs b/src/libraries/System.Private.CoreLib/src/System/FormatException.cs index 36bb7db6db6211..5fabf9dea933ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/FormatException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/FormatException.cs @@ -21,13 +21,13 @@ public FormatException() } public FormatException(string? message) - : base(message) + : base(message ?? SR.Arg_FormatException) { HResult = HResults.COR_E_FORMAT; } public FormatException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_FormatException, innerException) { HResult = HResults.COR_E_FORMAT; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureNotFoundException.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureNotFoundException.cs index 49efb6232591e1..5b9b65d3717aa7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureNotFoundException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureNotFoundException.cs @@ -20,40 +20,40 @@ public CultureNotFoundException() } public CultureNotFoundException(string? message) - : base(message) + : base(message ?? DefaultMessage) { } public CultureNotFoundException(string? paramName, string? message) - : base(message, paramName) + : base(message ?? DefaultMessage, paramName) { } public CultureNotFoundException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? DefaultMessage, innerException) { } public CultureNotFoundException(string? paramName, string? invalidCultureName, string? message) - : base(message, paramName) + : base(message ?? DefaultMessage, paramName) { _invalidCultureName = invalidCultureName; } public CultureNotFoundException(string? message, string? invalidCultureName, Exception? innerException) - : base(message, innerException) + : base(message ?? DefaultMessage, innerException) { _invalidCultureName = invalidCultureName; } public CultureNotFoundException(string? message, int invalidCultureId, Exception? innerException) - : base(message, innerException) + : base(message ?? DefaultMessage, innerException) { _invalidCultureId = invalidCultureId; } public CultureNotFoundException(string? paramName, int invalidCultureId, string? message) - : base(message, paramName) + : base(message ?? DefaultMessage, paramName) { _invalidCultureId = invalidCultureId; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryNotFoundException.cs b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryNotFoundException.cs index 0fe377e665456c..ca651ad4c27fc4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryNotFoundException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/DirectoryNotFoundException.cs @@ -24,13 +24,13 @@ public DirectoryNotFoundException() } public DirectoryNotFoundException(string? message) - : base(message) + : base(message ?? SR.Arg_DirectoryNotFoundException) { HResult = HResults.COR_E_DIRECTORYNOTFOUND; } public DirectoryNotFoundException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_DirectoryNotFoundException, innerException) { HResult = HResults.COR_E_DIRECTORYNOTFOUND; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/EndOfStreamException.cs b/src/libraries/System.Private.CoreLib/src/System/IO/EndOfStreamException.cs index bc3e2e07fe2541..5905204b7ae2c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/EndOfStreamException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/EndOfStreamException.cs @@ -18,13 +18,13 @@ public EndOfStreamException() } public EndOfStreamException(string? message) - : base(message) + : base(message ?? SR.Arg_EndOfStreamException) { HResult = HResults.COR_E_ENDOFSTREAM; } public EndOfStreamException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_EndOfStreamException, innerException) { HResult = HResults.COR_E_ENDOFSTREAM; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/InvalidDataException.cs b/src/libraries/System.Private.CoreLib/src/System/IO/InvalidDataException.cs index d928c92170aa13..dda0530108eb0c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/InvalidDataException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/InvalidDataException.cs @@ -23,7 +23,7 @@ public InvalidDataException() /// The error message that explains the reason for the exception. /// This constructor initializes the property of the new instance to a system-supplied message that describes the error, such as "An invalid argument was specified." This message is localized based on the current system culture. public InvalidDataException(string? message) - : base(message) + : base(message ?? SR.GenericInvalidData) { } @@ -33,7 +33,7 @@ public InvalidDataException(string? message) /// This constructor initializes the property of the new instance using the value of the parameter. The content of the parameter is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture. /// An exception that is thrown as a direct result of a previous exception should include a reference to the previous exception in the property. The property returns the same value that is passed into the constructor, or if the property does not supply the inner exception value to the constructor. public InvalidDataException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.GenericInvalidData, innerException) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PathTooLongException.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PathTooLongException.cs index 280d59991556e9..5e9cbf72a70a39 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/PathTooLongException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PathTooLongException.cs @@ -18,13 +18,13 @@ public PathTooLongException() } public PathTooLongException(string? message) - : base(message) + : base(message ?? SR.IO_PathTooLong) { HResult = HResults.COR_E_PATHTOOLONG; } public PathTooLongException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.IO_PathTooLong, innerException) { HResult = HResults.COR_E_PATHTOOLONG; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOutOfRangeException.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOutOfRangeException.cs index d5ab3d4648a02d..b80408e40779ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOutOfRangeException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOutOfRangeException.cs @@ -20,13 +20,13 @@ public IndexOutOfRangeException() } public IndexOutOfRangeException(string? message) - : base(message) + : base(message ?? SR.Arg_IndexOutOfRangeException) { HResult = HResults.COR_E_INDEXOUTOFRANGE; } public IndexOutOfRangeException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_IndexOutOfRangeException, innerException) { HResult = HResults.COR_E_INDEXOUTOFRANGE; } diff --git a/src/libraries/System.Private.CoreLib/src/System/InsufficientExecutionStackException.cs b/src/libraries/System.Private.CoreLib/src/System/InsufficientExecutionStackException.cs index b6a8d865055ad9..3a0b1a5e214f1e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/InsufficientExecutionStackException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/InsufficientExecutionStackException.cs @@ -17,13 +17,13 @@ public InsufficientExecutionStackException() } public InsufficientExecutionStackException(string? message) - : base(message) + : base(message ?? SR.Arg_InsufficientExecutionStackException) { HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } public InsufficientExecutionStackException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_InsufficientExecutionStackException, innerException) { HResult = HResults.COR_E_INSUFFICIENTEXECUTIONSTACK; } diff --git a/src/libraries/System.Private.CoreLib/src/System/InsufficientMemoryException.cs b/src/libraries/System.Private.CoreLib/src/System/InsufficientMemoryException.cs index c2ba90170cf9f9..30befd22fa2f15 100644 --- a/src/libraries/System.Private.CoreLib/src/System/InsufficientMemoryException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/InsufficientMemoryException.cs @@ -18,29 +18,30 @@ namespace System [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class InsufficientMemoryException : OutOfMemoryException { - public InsufficientMemoryException() : base( -#if CORECLR - GetMessageFromNativeResources(ExceptionMessageKind.OutOfMemory) -#else - SR.Arg_OutOfMemoryException -#endif - ) + public InsufficientMemoryException() : base(GetDefaultMessage()) { HResult = HResults.COR_E_INSUFFICIENTMEMORY; } public InsufficientMemoryException(string? message) - : base(message) + : base(message ?? GetDefaultMessage()) { HResult = HResults.COR_E_INSUFFICIENTMEMORY; } public InsufficientMemoryException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? GetDefaultMessage(), innerException) { HResult = HResults.COR_E_INSUFFICIENTMEMORY; } + private static string GetDefaultMessage() +#if CORECLR + => GetMessageFromNativeResources(ExceptionMessageKind.OutOfMemory); +#else + => SR.Arg_OutOfMemoryException; +#endif + [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] private InsufficientMemoryException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/src/libraries/System.Private.CoreLib/src/System/InvalidCastException.cs b/src/libraries/System.Private.CoreLib/src/System/InvalidCastException.cs index 7e97f583a3ef13..024717bfe70df8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/InvalidCastException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/InvalidCastException.cs @@ -21,19 +21,19 @@ public InvalidCastException() } public InvalidCastException(string? message) - : base(message) + : base(message ?? SR.Arg_InvalidCastException) { HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_InvalidCastException, innerException) { HResult = HResults.COR_E_INVALIDCAST; } public InvalidCastException(string? message, int errorCode) - : base(message) + : base(message ?? SR.Arg_InvalidCastException) { HResult = errorCode; } diff --git a/src/libraries/System.Private.CoreLib/src/System/InvalidOperationException.cs b/src/libraries/System.Private.CoreLib/src/System/InvalidOperationException.cs index 97233835319c0f..394e5c78e533f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/InvalidOperationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/InvalidOperationException.cs @@ -21,13 +21,13 @@ public InvalidOperationException() } public InvalidOperationException(string? message) - : base(message) + : base(message ?? SR.Arg_InvalidOperationException) { HResult = HResults.COR_E_INVALIDOPERATION; } public InvalidOperationException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_InvalidOperationException, innerException) { HResult = HResults.COR_E_INVALIDOPERATION; } diff --git a/src/libraries/System.Private.CoreLib/src/System/InvalidProgramException.cs b/src/libraries/System.Private.CoreLib/src/System/InvalidProgramException.cs index c59cad49ac3a02..0cda622cb22228 100644 --- a/src/libraries/System.Private.CoreLib/src/System/InvalidProgramException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/InvalidProgramException.cs @@ -21,13 +21,13 @@ public InvalidProgramException() } public InvalidProgramException(string? message) - : base(message) + : base(message ?? SR.InvalidProgram_Default) { HResult = HResults.COR_E_INVALIDPROGRAM; } public InvalidProgramException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.InvalidProgram_Default, inner) { HResult = HResults.COR_E_INVALIDPROGRAM; } diff --git a/src/libraries/System.Private.CoreLib/src/System/MemberAccessException.cs b/src/libraries/System.Private.CoreLib/src/System/MemberAccessException.cs index 9e4cbdf085cf35..1d1839a14596fb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemberAccessException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemberAccessException.cs @@ -33,13 +33,13 @@ public MemberAccessException() // and its ExceptionInfo reference set to null. // public MemberAccessException(string? message) - : base(message) + : base(message ?? SR.Arg_AccessException) { HResult = HResults.COR_E_MEMBERACCESS; } public MemberAccessException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_AccessException, inner) { HResult = HResults.COR_E_MEMBERACCESS; } diff --git a/src/libraries/System.Private.CoreLib/src/System/MethodAccessException.cs b/src/libraries/System.Private.CoreLib/src/System/MethodAccessException.cs index 1777f153f38069..910db3d80d160c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MethodAccessException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MethodAccessException.cs @@ -21,13 +21,13 @@ public MethodAccessException() } public MethodAccessException(string? message) - : base(message) + : base(message ?? SR.Arg_MethodAccessException) { HResult = HResults.COR_E_METHODACCESS; } public MethodAccessException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MethodAccessException, inner) { HResult = HResults.COR_E_METHODACCESS; } diff --git a/src/libraries/System.Private.CoreLib/src/System/MissingFieldException.cs b/src/libraries/System.Private.CoreLib/src/System/MissingFieldException.cs index 7d64f0f11aa479..2a216333de4932 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MissingFieldException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MissingFieldException.cs @@ -18,13 +18,13 @@ public MissingFieldException() } public MissingFieldException(string? message) - : base(message) + : base(message ?? SR.Arg_MissingFieldException) { HResult = HResults.COR_E_MISSINGFIELD; } public MissingFieldException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MissingFieldException, inner) { HResult = HResults.COR_E_MISSINGFIELD; } diff --git a/src/libraries/System.Private.CoreLib/src/System/MissingMemberException.cs b/src/libraries/System.Private.CoreLib/src/System/MissingMemberException.cs index 4fb2cb230d64ce..bc2e368efa1799 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MissingMemberException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MissingMemberException.cs @@ -18,13 +18,13 @@ public MissingMemberException() } public MissingMemberException(string? message) - : base(message) + : base(message ?? SR.Arg_MissingMemberException) { HResult = HResults.COR_E_MISSINGMEMBER; } public MissingMemberException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MissingMemberException, inner) { HResult = HResults.COR_E_MISSINGMEMBER; } diff --git a/src/libraries/System.Private.CoreLib/src/System/MissingMethodException.cs b/src/libraries/System.Private.CoreLib/src/System/MissingMethodException.cs index 9399470cef91a5..b9bf68c4f91a00 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MissingMethodException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MissingMethodException.cs @@ -21,13 +21,13 @@ public MissingMethodException() } public MissingMethodException(string? message) - : base(message) + : base(message ?? SR.Arg_MissingMethodException) { HResult = HResults.COR_E_MISSINGMETHOD; } public MissingMethodException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MissingMethodException, inner) { HResult = HResults.COR_E_MISSINGMETHOD; } diff --git a/src/libraries/System.Private.CoreLib/src/System/MulticastNotSupportedException.cs b/src/libraries/System.Private.CoreLib/src/System/MulticastNotSupportedException.cs index 6f022c61305e7a..01b2c66f2934b8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MulticastNotSupportedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MulticastNotSupportedException.cs @@ -22,13 +22,13 @@ public MulticastNotSupportedException() } public MulticastNotSupportedException(string? message) - : base(message) + : base(message ?? SR.Arg_MulticastNotSupportedException) { HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } public MulticastNotSupportedException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MulticastNotSupportedException, inner) { HResult = HResults.COR_E_MULTICASTNOTSUPPORTED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/NotFiniteNumberException.cs b/src/libraries/System.Private.CoreLib/src/System/NotFiniteNumberException.cs index 88fe578c51dc0a..558a29543cd13c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/NotFiniteNumberException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/NotFiniteNumberException.cs @@ -27,27 +27,27 @@ public NotFiniteNumberException(double offendingNumber) } public NotFiniteNumberException(string? message) - : base(message) + : base(message ?? SR.Arg_NotFiniteNumberException) { _offendingNumber = 0; HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(string? message, double offendingNumber) - : base(message) + : base(message ?? SR.Arg_NotFiniteNumberException) { _offendingNumber = offendingNumber; HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_NotFiniteNumberException, innerException) { HResult = HResults.COR_E_NOTFINITENUMBER; } public NotFiniteNumberException(string? message, double offendingNumber, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_NotFiniteNumberException, innerException) { _offendingNumber = offendingNumber; HResult = HResults.COR_E_NOTFINITENUMBER; diff --git a/src/libraries/System.Private.CoreLib/src/System/NotImplementedException.cs b/src/libraries/System.Private.CoreLib/src/System/NotImplementedException.cs index 17062b45a4557e..383ad0e76300ba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/NotImplementedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/NotImplementedException.cs @@ -20,12 +20,12 @@ public NotImplementedException() HResult = HResults.E_NOTIMPL; } public NotImplementedException(string? message) - : base(message) + : base(message ?? SR.Arg_NotImplementedException) { HResult = HResults.E_NOTIMPL; } public NotImplementedException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_NotImplementedException, inner) { HResult = HResults.E_NOTIMPL; } diff --git a/src/libraries/System.Private.CoreLib/src/System/NotSupportedException.cs b/src/libraries/System.Private.CoreLib/src/System/NotSupportedException.cs index 40e638d01b7b54..e1278c6f3fbe18 100644 --- a/src/libraries/System.Private.CoreLib/src/System/NotSupportedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/NotSupportedException.cs @@ -22,13 +22,13 @@ public NotSupportedException() } public NotSupportedException(string? message) - : base(message) + : base(message ?? SR.Arg_NotSupportedException) { HResult = HResults.COR_E_NOTSUPPORTED; } public NotSupportedException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_NotSupportedException, innerException) { HResult = HResults.COR_E_NOTSUPPORTED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/NullReferenceException.cs b/src/libraries/System.Private.CoreLib/src/System/NullReferenceException.cs index 095b4310627fe9..ed414e6b8527f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/NullReferenceException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/NullReferenceException.cs @@ -21,13 +21,13 @@ public NullReferenceException() } public NullReferenceException(string? message) - : base(message) + : base(message ?? SR.Arg_NullReferenceException) { HResult = HResults.E_POINTER; } public NullReferenceException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_NullReferenceException, innerException) { HResult = HResults.E_POINTER; } diff --git a/src/libraries/System.Private.CoreLib/src/System/ObjectDisposedException.cs b/src/libraries/System.Private.CoreLib/src/System/ObjectDisposedException.cs index e0360d7eddca6e..e7098478a64909 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ObjectDisposedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ObjectDisposedException.cs @@ -29,14 +29,14 @@ public ObjectDisposedException(string? objectName) : { } - public ObjectDisposedException(string? objectName, string? message) : base(message) + public ObjectDisposedException(string? objectName, string? message) : base(message ?? SR.ObjectDisposed_Generic) { HResult = HResults.COR_E_OBJECTDISPOSED; _objectName = objectName; } public ObjectDisposedException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.ObjectDisposed_Generic, innerException) { HResult = HResults.COR_E_OBJECTDISPOSED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/OperationCanceledException.cs b/src/libraries/System.Private.CoreLib/src/System/OperationCanceledException.cs index 2a01950c0df8d3..884f43b7797740 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OperationCanceledException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OperationCanceledException.cs @@ -31,13 +31,13 @@ public OperationCanceledException() } public OperationCanceledException(string? message) - : base(message) + : base(message ?? SR.OperationCanceled) { HResult = HResults.COR_E_OPERATIONCANCELED; } public OperationCanceledException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.OperationCanceled, innerException) { HResult = HResults.COR_E_OPERATIONCANCELED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/OutOfMemoryException.cs b/src/libraries/System.Private.CoreLib/src/System/OutOfMemoryException.cs index f6f6430ec05294..a9615f363730a0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OutOfMemoryException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OutOfMemoryException.cs @@ -14,29 +14,30 @@ namespace System [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class OutOfMemoryException : SystemException { - public OutOfMemoryException() : base( -#if CORECLR - GetMessageFromNativeResources(ExceptionMessageKind.OutOfMemory) -#else - SR.Arg_OutOfMemoryException -#endif - ) + public OutOfMemoryException() : base(GetDefaultMessage()) { HResult = HResults.COR_E_OUTOFMEMORY; } public OutOfMemoryException(string? message) - : base(message) + : base(message ?? GetDefaultMessage()) { HResult = HResults.COR_E_OUTOFMEMORY; } public OutOfMemoryException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? GetDefaultMessage(), innerException) { HResult = HResults.COR_E_OUTOFMEMORY; } + private static string GetDefaultMessage() +#if CORECLR + => GetMessageFromNativeResources(ExceptionMessageKind.OutOfMemory); +#else + => SR.Arg_OutOfMemoryException; +#endif + [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] protected OutOfMemoryException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/libraries/System.Private.CoreLib/src/System/OverflowException.cs b/src/libraries/System.Private.CoreLib/src/System/OverflowException.cs index 04cdf39076c863..5374bd19b5c939 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OverflowException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OverflowException.cs @@ -21,13 +21,13 @@ public OverflowException() } public OverflowException(string? message) - : base(message) + : base(message ?? SR.Arg_OverflowException) { HResult = HResults.COR_E_OVERFLOW; } public OverflowException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_OverflowException, innerException) { HResult = HResults.COR_E_OVERFLOW; } diff --git a/src/libraries/System.Private.CoreLib/src/System/PlatformNotSupportedException.cs b/src/libraries/System.Private.CoreLib/src/System/PlatformNotSupportedException.cs index 26932e64ac2c2c..ef0bd733f7ee76 100644 --- a/src/libraries/System.Private.CoreLib/src/System/PlatformNotSupportedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/PlatformNotSupportedException.cs @@ -21,13 +21,13 @@ public PlatformNotSupportedException() } public PlatformNotSupportedException(string? message) - : base(message) + : base(message ?? SR.Arg_PlatformNotSupported) { HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } public PlatformNotSupportedException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_PlatformNotSupported, inner) { HResult = HResults.COR_E_PLATFORMNOTSUPPORTED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/RankException.cs b/src/libraries/System.Private.CoreLib/src/System/RankException.cs index 0db381b36d422b..afd4d07833a8a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/RankException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/RankException.cs @@ -21,13 +21,13 @@ public RankException() } public RankException(string? message) - : base(message) + : base(message ?? SR.Arg_RankException) { HResult = HResults.COR_E_RANK; } public RankException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_RankException, innerException) { HResult = HResults.COR_E_RANK; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs index 6e75c436efa3a3..23cfbf2dd43b83 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs @@ -17,13 +17,13 @@ public AmbiguousMatchException() } public AmbiguousMatchException(string? message) - : base(message) + : base(message ?? SR.Arg_AmbiguousMatchException_NoMessage) { HResult = HResults.COR_E_AMBIGUOUSMATCH; } public AmbiguousMatchException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_AmbiguousMatchException_NoMessage, inner) { HResult = HResults.COR_E_AMBIGUOUSMATCH; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeFormatException.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeFormatException.cs index b70a1c471b7100..8a6d02e76c0621 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeFormatException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeFormatException.cs @@ -22,7 +22,7 @@ public CustomAttributeFormatException(string? message) } public CustomAttributeFormatException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_CustomAttributeFormatException, inner) { HResult = HResults.COR_E_CUSTOMATTRIBUTEFORMAT; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvalidFilterCriteriaException.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvalidFilterCriteriaException.cs index 5efdf3ee6f2d14..f64932d2677e65 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvalidFilterCriteriaException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvalidFilterCriteriaException.cs @@ -22,7 +22,7 @@ public InvalidFilterCriteriaException(string? message) } public InvalidFilterCriteriaException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_InvalidFilterCriteriaException, inner) { HResult = HResults.COR_E_INVALIDFILTERCRITERIA; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetInvocationException.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetInvocationException.cs index 04219ef4b90bb3..5bc0e1e35c9244 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetInvocationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetInvocationException.cs @@ -17,7 +17,7 @@ public TargetInvocationException(Exception? inner) } public TargetInvocationException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_TargetInvocationException, inner) { HResult = HResults.COR_E_TARGETINVOCATION; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetParameterCountException.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetParameterCountException.cs index be8841c7a7ed96..4962492b59a3d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetParameterCountException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/TargetParameterCountException.cs @@ -17,13 +17,13 @@ public TargetParameterCountException() } public TargetParameterCountException(string? message) - : base(message) + : base(message ?? SR.Arg_TargetParameterCountException) { HResult = HResults.COR_E_TARGETPARAMCOUNT; } public TargetParameterCountException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_TargetParameterCountException, inner) { HResult = HResults.COR_E_TARGETPARAMCOUNT; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/MissingManifestResourceException.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/MissingManifestResourceException.cs index 05c204d2d04b99..3b095e92ac8e6c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/MissingManifestResourceException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/MissingManifestResourceException.cs @@ -19,13 +19,13 @@ public MissingManifestResourceException() } public MissingManifestResourceException(string? message) - : base(message) + : base(message ?? SR.Arg_MissingManifestResourceException) { HResult = HResults.COR_E_MISSINGMANIFESTRESOURCE; } public MissingManifestResourceException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MissingManifestResourceException, inner) { HResult = HResults.COR_E_MISSINGMANIFESTRESOURCE; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/MissingSatelliteAssemblyException.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/MissingSatelliteAssemblyException.cs index 2e1c0bd2af84d6..fb60055555ec6c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/MissingSatelliteAssemblyException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/MissingSatelliteAssemblyException.cs @@ -23,20 +23,20 @@ public MissingSatelliteAssemblyException() } public MissingSatelliteAssemblyException(string? message) - : base(message) + : base(message ?? SR.MissingSatelliteAssembly_Default) { HResult = HResults.COR_E_MISSINGSATELLITEASSEMBLY; } public MissingSatelliteAssemblyException(string? message, string? cultureName) - : base(message) + : base(message ?? SR.MissingSatelliteAssembly_Default) { HResult = HResults.COR_E_MISSINGSATELLITEASSEMBLY; _cultureName = cultureName; } public MissingSatelliteAssemblyException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.MissingSatelliteAssembly_Default, inner) { HResult = HResults.COR_E_MISSINGSATELLITEASSEMBLY; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs index d5a85c2eef3ace..efcc70be299177 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs @@ -17,13 +17,13 @@ public AmbiguousImplementationException() } public AmbiguousImplementationException(string? message) - : base(message) + : base(message ?? SR.Arg_AmbiguousImplementationException_NoMessage) { HResult = HResults.COR_E_AMBIGUOUSIMPLEMENTATION; } public AmbiguousImplementationException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_AmbiguousImplementationException_NoMessage, innerException) { HResult = HResults.COR_E_AMBIGUOUSIMPLEMENTATION; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/SwitchExpressionException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/SwitchExpressionException.cs index b9a358eb538ecf..b6d4206a80078e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/SwitchExpressionException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/SwitchExpressionException.cs @@ -35,10 +35,10 @@ private SwitchExpressionException(SerializationInfo info, StreamingContext conte UnmatchedValue = info.GetValue(nameof(UnmatchedValue), typeof(object)); } - public SwitchExpressionException(string? message) : base(message) { } + public SwitchExpressionException(string? message) : base(message ?? SR.Arg_SwitchExpressionException) { } public SwitchExpressionException(string? message, Exception? innerException) - : base(message, innerException) { } + : base(message ?? SR.Arg_SwitchExpressionException, innerException) { } public object? UnmatchedValue { get; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs index c74821e21c6851..5a1f5df90270af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/COMException.cs @@ -24,19 +24,19 @@ public COMException() } public COMException(string? message) - : base(message) + : base(message ?? SR.Arg_COMException) { HResult = HResults.E_FAIL; } public COMException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_COMException, inner) { HResult = HResults.E_FAIL; } public COMException(string? message, int errorCode) - : base(message) + : base(message ?? SR.Arg_COMException) { HResult = errorCode; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs index 6f148661d849ef..7285e4c175fe3d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ExternalException.cs @@ -21,19 +21,19 @@ public ExternalException() } public ExternalException(string? message) - : base(message) + : base(message ?? SR.Arg_ExternalException) { HResult = HResults.E_FAIL; } public ExternalException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_ExternalException, inner) { HResult = HResults.E_FAIL; } public ExternalException(string? message, int errorCode) - : base(message) + : base(message ?? SR.Arg_ExternalException) { HResult = errorCode; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidComObjectException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidComObjectException.cs index 4090c2f25437b1..b618434c0f9de0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidComObjectException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidComObjectException.cs @@ -23,13 +23,13 @@ public InvalidComObjectException() } public InvalidComObjectException(string? message) - : base(message) + : base(message ?? SR.Arg_InvalidComObjectException) { HResult = HResults.COR_E_INVALIDCOMOBJECT; } public InvalidComObjectException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_InvalidComObjectException, inner) { HResult = HResults.COR_E_INVALIDCOMOBJECT; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs index 1f900d58a4397b..705a46eecac542 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/InvalidOleVariantTypeException.cs @@ -22,13 +22,13 @@ public InvalidOleVariantTypeException() } public InvalidOleVariantTypeException(string? message) - : base(message) + : base(message ?? SR.Arg_InvalidOleVariantTypeException) { HResult = HResults.COR_E_INVALIDOLEVARIANTTYPE; } public InvalidOleVariantTypeException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_InvalidOleVariantTypeException, inner) { HResult = HResults.COR_E_INVALIDOLEVARIANTTYPE; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs index d6f2a78cdfa74f..98bcd9ba285260 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MarshalDirectiveException.cs @@ -21,13 +21,13 @@ public MarshalDirectiveException() } public MarshalDirectiveException(string? message) - : base(message) + : base(message ?? SR.Arg_MarshalDirectiveException) { HResult = HResults.COR_E_MARSHALDIRECTIVE; } public MarshalDirectiveException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_MarshalDirectiveException, inner) { HResult = HResults.COR_E_MARSHALDIRECTIVE; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs index 37e1a657931c1f..80646bb513ba9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayRankMismatchException.cs @@ -22,13 +22,13 @@ public SafeArrayRankMismatchException() } public SafeArrayRankMismatchException(string? message) - : base(message) + : base(message ?? SR.Arg_SafeArrayRankMismatchException) { HResult = HResults.COR_E_SAFEARRAYRANKMISMATCH; } public SafeArrayRankMismatchException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_SafeArrayRankMismatchException, inner) { HResult = HResults.COR_E_SAFEARRAYRANKMISMATCH; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs index 4211cc93e6aed5..8434bd4781296e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeArrayTypeMismatchException.cs @@ -22,13 +22,13 @@ public SafeArrayTypeMismatchException() } public SafeArrayTypeMismatchException(string? message) - : base(message) + : base(message ?? SR.Arg_SafeArrayTypeMismatchException) { HResult = HResults.COR_E_SAFEARRAYTYPEMISMATCH; } public SafeArrayTypeMismatchException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_SafeArrayTypeMismatchException, inner) { HResult = HResults.COR_E_SAFEARRAYTYPEMISMATCH; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationException.cs index 4831d57cd469eb..a25de2ea80e515 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Serialization/SerializationException.cs @@ -21,13 +21,13 @@ public SerializationException() } public SerializationException(string? message) - : base(message) + : base(message ?? SR.SerializationException) { HResult = HResults.COR_E_SERIALIZATION; } public SerializationException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.SerializationException, innerException) { HResult = HResults.COR_E_SERIALIZATION; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/CryptographicException.cs b/src/libraries/System.Private.CoreLib/src/System/Security/CryptographicException.cs index 6c8eab8a896f4d..14bf748bcc810e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/CryptographicException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/CryptographicException.cs @@ -24,12 +24,12 @@ public CryptographicException(int hr) } public CryptographicException(string? message) - : base(message) + : base(message ?? SR.Arg_CryptographyException) { } public CryptographicException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_CryptographyException, inner) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SecurityException.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SecurityException.cs index 786348af0329a9..f82d44c186b88d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SecurityException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/SecurityException.cs @@ -26,26 +26,26 @@ public SecurityException() } public SecurityException(string? message) - : base(message) + : base(message ?? SR.Arg_SecurityException) { HResult = HResults.COR_E_SECURITY; } public SecurityException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_SecurityException, inner) { HResult = HResults.COR_E_SECURITY; } public SecurityException(string? message, Type? type) - : base(message) + : base(message ?? SR.Arg_SecurityException) { HResult = HResults.COR_E_SECURITY; PermissionType = type; } public SecurityException(string? message, Type? type, string? state) - : base(message) + : base(message ?? SR.Arg_SecurityException) { HResult = HResults.COR_E_SECURITY; PermissionType = type; diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/VerificationException.cs b/src/libraries/System.Private.CoreLib/src/System/Security/VerificationException.cs index d91ce07a341e3b..b82a607ec4bec1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/VerificationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/VerificationException.cs @@ -18,13 +18,13 @@ public VerificationException() } public VerificationException(string? message) - : base(message) + : base(message ?? SR.Verification_Exception) { HResult = HResults.COR_E_VERIFICATION; } public VerificationException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Verification_Exception, innerException) { HResult = HResults.COR_E_VERIFICATION; } diff --git a/src/libraries/System.Private.CoreLib/src/System/StackOverflowException.cs b/src/libraries/System.Private.CoreLib/src/System/StackOverflowException.cs index 9d0b2ed65b56c7..c43e4a1a5e9c82 100644 --- a/src/libraries/System.Private.CoreLib/src/System/StackOverflowException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/StackOverflowException.cs @@ -20,13 +20,13 @@ public StackOverflowException() } public StackOverflowException(string? message) - : base(message) + : base(message ?? SR.Arg_StackOverflowException) { HResult = HResults.COR_E_STACKOVERFLOW; } public StackOverflowException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_StackOverflowException, innerException) { HResult = HResults.COR_E_STACKOVERFLOW; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/AbandonedMutexException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/AbandonedMutexException.cs index f7cfe86f3b6463..ca0b897bcdf72d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/AbandonedMutexException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/AbandonedMutexException.cs @@ -21,13 +21,13 @@ public AbandonedMutexException() } public AbandonedMutexException(string? message) - : base(message) + : base(message ?? SR.Threading_AbandonedMutexException) { HResult = HResults.COR_E_ABANDONEDMUTEX; } public AbandonedMutexException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Threading_AbandonedMutexException, inner) { HResult = HResults.COR_E_ABANDONEDMUTEX; } @@ -40,14 +40,14 @@ public AbandonedMutexException(int location, WaitHandle? handle) } public AbandonedMutexException(string? message, int location, WaitHandle? handle) - : base(message) + : base(message ?? SR.Threading_AbandonedMutexException) { HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); } public AbandonedMutexException(string? message, Exception? inner, int location, WaitHandle? handle) - : base(message, inner) + : base(message ?? SR.Threading_AbandonedMutexException, inner) { HResult = HResults.COR_E_ABANDONEDMUTEX; SetupException(location, handle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreFullException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreFullException.cs index 339e637580af8e..221c70f8b19019 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreFullException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreFullException.cs @@ -15,11 +15,11 @@ public SemaphoreFullException() : base(SR.Threading_SemaphoreFullException) { } - public SemaphoreFullException(string? message) : base(message) + public SemaphoreFullException(string? message) : base(message ?? SR.Threading_SemaphoreFullException) { } - public SemaphoreFullException(string? message, Exception? innerException) : base(message, innerException) + public SemaphoreFullException(string? message, Exception? innerException) : base(message ?? SR.Threading_SemaphoreFullException, innerException) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SynchronizationLockException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SynchronizationLockException.cs index 158a3fdb3ab7d7..0aa4eee04f0386 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SynchronizationLockException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SynchronizationLockException.cs @@ -21,13 +21,13 @@ public SynchronizationLockException() } public SynchronizationLockException(string? message) - : base(message) + : base(message ?? SR.Arg_SynchronizationLockException) { HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } public SynchronizationLockException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_SynchronizationLockException, innerException) { HResult = HResults.COR_E_SYNCHRONIZATIONLOCK; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCanceledException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCanceledException.cs index 16c2c779da4eaa..bade3751d7aa99 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCanceledException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCanceledException.cs @@ -37,7 +37,7 @@ public TaskCanceledException() : base(SR.TaskCanceledException_ctor_DefaultMessa /// class with a specified error message. /// /// The error message that explains the reason for the exception. - public TaskCanceledException(string? message) : base(message) + public TaskCanceledException(string? message) : base(message ?? SR.TaskCanceledException_ctor_DefaultMessage) { } @@ -48,7 +48,7 @@ public TaskCanceledException(string? message) : base(message) /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. - public TaskCanceledException(string? message, Exception? innerException) : base(message, innerException) + public TaskCanceledException(string? message, Exception? innerException) : base(message ?? SR.TaskCanceledException_ctor_DefaultMessage, innerException) { } @@ -60,7 +60,7 @@ public TaskCanceledException(string? message, Exception? innerException) : base( /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. /// The that triggered the cancellation. - public TaskCanceledException(string? message, Exception? innerException, CancellationToken token) : base(message, innerException, token) + public TaskCanceledException(string? message, Exception? innerException, CancellationToken token) : base(message ?? SR.TaskCanceledException_ctor_DefaultMessage, innerException, token) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskSchedulerException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskSchedulerException.cs index d935bbcc696461..8615855f15dd9c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskSchedulerException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskSchedulerException.cs @@ -35,7 +35,7 @@ public TaskSchedulerException() : base(SR.TaskSchedulerException_ctor_DefaultMes /// class with a specified error message. /// /// The error message that explains the reason for the exception. - public TaskSchedulerException(string? message) : base(message) + public TaskSchedulerException(string? message) : base(message ?? SR.TaskSchedulerException_ctor_DefaultMessage) { } @@ -57,7 +57,7 @@ public TaskSchedulerException(Exception? innerException) /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception. - public TaskSchedulerException(string? message, Exception? innerException) : base(message, innerException) + public TaskSchedulerException(string? message, Exception? innerException) : base(message ?? SR.TaskSchedulerException_ctor_DefaultMessage, innerException) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs index 193747c944c343..927f1fa6d84f0e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadInterruptedException.cs @@ -14,29 +14,30 @@ namespace System.Threading [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class ThreadInterruptedException : SystemException { - public ThreadInterruptedException() : base( -#if CORECLR - GetMessageFromNativeResources(ExceptionMessageKind.ThreadInterrupted) -#else - SR.Threading_ThreadInterrupted -#endif - ) + public ThreadInterruptedException() : base(GetDefaultMessage()) { HResult = HResults.COR_E_THREADINTERRUPTED; } public ThreadInterruptedException(string? message) - : base(message) + : base(message ?? GetDefaultMessage()) { HResult = HResults.COR_E_THREADINTERRUPTED; } public ThreadInterruptedException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? GetDefaultMessage(), innerException) { HResult = HResults.COR_E_THREADINTERRUPTED; } + private static string GetDefaultMessage() +#if CORECLR + => GetMessageFromNativeResources(ExceptionMessageKind.ThreadInterrupted); +#else + => SR.Threading_ThreadInterrupted; +#endif + [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] protected ThreadInterruptedException(SerializationInfo info, StreamingContext context) : base(info, context) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadStateException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadStateException.cs index 81118f6dee0eda..6a3cd0d0b6d383 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadStateException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadStateException.cs @@ -21,13 +21,13 @@ public ThreadStateException() } public ThreadStateException(string? message) - : base(message) + : base(message ?? SR.Arg_ThreadStateException) { HResult = HResults.COR_E_THREADSTATE; } public ThreadStateException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_ThreadStateException, innerException) { HResult = HResults.COR_E_THREADSTATE; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandleCannotBeOpenedException.cs index 2babfd6e213467..bd4d74cb513c0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandleCannotBeOpenedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandleCannotBeOpenedException.cs @@ -16,12 +16,12 @@ public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotB HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } - public WaitHandleCannotBeOpenedException(string? message) : base(message) + public WaitHandleCannotBeOpenedException(string? message) : base(message ?? SR.Threading_WaitHandleCannotBeOpenedException) { HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } - public WaitHandleCannotBeOpenedException(string? message, Exception? innerException) : base(message, innerException) + public WaitHandleCannotBeOpenedException(string? message, Exception? innerException) : base(message ?? SR.Threading_WaitHandleCannotBeOpenedException, innerException) { HResult = HResults.COR_E_WAITHANDLECANNOTBEOPENED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeoutException.cs b/src/libraries/System.Private.CoreLib/src/System/TimeoutException.cs index 29f6155094e03e..ffe14b6898db4a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeoutException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeoutException.cs @@ -21,13 +21,13 @@ public TimeoutException() } public TimeoutException(string? message) - : base(message) + : base(message ?? SR.Arg_TimeoutException) { HResult = HResults.COR_E_TIMEOUT; } public TimeoutException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_TimeoutException, innerException) { HResult = HResults.COR_E_TIMEOUT; } diff --git a/src/libraries/System.Private.CoreLib/src/System/TypeAccessException.cs b/src/libraries/System.Private.CoreLib/src/System/TypeAccessException.cs index 4068ac3b816025..418efbe9e28441 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TypeAccessException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TypeAccessException.cs @@ -20,13 +20,13 @@ public TypeAccessException() } public TypeAccessException(string? message) - : base(message) + : base(message ?? SR.Arg_TypeAccessException) { HResult = HResults.COR_E_TYPEACCESS; } public TypeAccessException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_TypeAccessException, inner) { HResult = HResults.COR_E_TYPEACCESS; } diff --git a/src/libraries/System.Private.CoreLib/src/System/TypeInitializationException.cs b/src/libraries/System.Private.CoreLib/src/System/TypeInitializationException.cs index 99994113a0d55c..912dd77a46f736 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TypeInitializationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TypeInitializationException.cs @@ -32,13 +32,13 @@ public TypeInitializationException(string? fullTypeName, Exception? innerExcepti // This is called from within the runtime. I believe this is necessary // for Interop only, though it's not particularly useful. - internal TypeInitializationException(string? message) : base(message) + internal TypeInitializationException(string? message) : base(message ?? SR.TypeInitialization_Default) { HResult = HResults.COR_E_TYPEINITIALIZATION; } internal TypeInitializationException(string? fullTypeName, string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.TypeInitialization_Default, innerException) { _typeName = fullTypeName; HResult = HResults.COR_E_TYPEINITIALIZATION; diff --git a/src/libraries/System.Private.CoreLib/src/System/TypeUnloadedException.cs b/src/libraries/System.Private.CoreLib/src/System/TypeUnloadedException.cs index aff385f678bfd5..d4fcc651db3752 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TypeUnloadedException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TypeUnloadedException.cs @@ -18,13 +18,13 @@ public TypeUnloadedException() } public TypeUnloadedException(string? message) - : base(message) + : base(message ?? SR.Arg_TypeUnloadedException) { HResult = HResults.COR_E_TYPEUNLOADED; } public TypeUnloadedException(string? message, Exception? innerException) - : base(message, innerException) + : base(message ?? SR.Arg_TypeUnloadedException, innerException) { HResult = HResults.COR_E_TYPEUNLOADED; } diff --git a/src/libraries/System.Private.CoreLib/src/System/UnauthorizedAccessException.cs b/src/libraries/System.Private.CoreLib/src/System/UnauthorizedAccessException.cs index 88d89e1fee271f..053d6fc39df915 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UnauthorizedAccessException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UnauthorizedAccessException.cs @@ -21,13 +21,13 @@ public UnauthorizedAccessException() } public UnauthorizedAccessException(string? message) - : base(message) + : base(message ?? SR.Arg_UnauthorizedAccessException) { HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } public UnauthorizedAccessException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_UnauthorizedAccessException, inner) { HResult = HResults.COR_E_UNAUTHORIZEDACCESS; } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs index 6c63045da0f130..e09b0ec149764e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs @@ -24,7 +24,7 @@ public AuthenticationTagMismatchException() : base(SR.Cryptography_AuthTagMismat /// /// The error message that explains the reason for the exception. /// - public AuthenticationTagMismatchException(string? message) : base(message) + public AuthenticationTagMismatchException(string? message) : base(message ?? SR.Cryptography_AuthTagMismatch) { } @@ -40,7 +40,7 @@ public AuthenticationTagMismatchException(string? message) : base(message) /// , the current exception is raised in a catch block that handles the inner exception. /// public AuthenticationTagMismatchException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Cryptography_AuthTagMismatch, inner) { } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs index 508b39fbcf6034..a5825728278ce0 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs @@ -17,12 +17,12 @@ public CryptographicUnexpectedOperationException() } public CryptographicUnexpectedOperationException(string? message) - : base(message) + : base(message ?? SR.Arg_CryptographyException) { } public CryptographicUnexpectedOperationException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.Arg_CryptographyException, inner) { } diff --git a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs index 157ac1ffe20efe..4a97e251123fe4 100644 --- a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs +++ b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IdentityNotMappedException.cs @@ -18,12 +18,12 @@ public IdentityNotMappedException() } public IdentityNotMappedException(string? message) - : base(message) + : base(message ?? SR.IdentityReference_IdentityNotMapped) { } public IdentityNotMappedException(string? message, Exception? inner) - : base(message, inner) + : base(message ?? SR.IdentityReference_IdentityNotMapped, inner) { } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs index aac7f3420da9ab..198bd6340a95a3 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexMatchTimeoutException.cs @@ -32,7 +32,7 @@ public RegexMatchTimeoutException(string regexInput, string regexPattern, TimeSp /// developers should prefer using the constructor /// public RegexMatchTimeoutException(string input, string pattern, TimeSpan matchTimeout). /// - public RegexMatchTimeoutException() { } + public RegexMatchTimeoutException() : base(SR.RegexMatchTimeoutException_Occurred) { } /// /// This constructor is provided in compliance with common .NET Framework design patterns; @@ -40,7 +40,7 @@ public RegexMatchTimeoutException() { } /// public RegexMatchTimeoutException(string input, string pattern, TimeSpan matchTimeout). /// /// The error message that explains the reason for the exception. - public RegexMatchTimeoutException(string message) : base(message) { } + public RegexMatchTimeoutException(string message) : base(message ?? SR.RegexMatchTimeoutException_Occurred) { } /// /// This constructor is provided in compliance with common .NET Framework design patterns; @@ -49,7 +49,7 @@ public RegexMatchTimeoutException(string message) : base(message) { } /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null. - public RegexMatchTimeoutException(string message, Exception inner) : base(message, inner) { } + public RegexMatchTimeoutException(string message, Exception inner) : base(message ?? SR.RegexMatchTimeoutException_Occurred, inner) { } [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs index b3c4151e133703..1a79350e5fd7e4 100644 --- a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs +++ b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.cs @@ -12,7 +12,7 @@ public ChannelClosedException() : /// Initializes a new instance of the class. /// The message that describes the error. - public ChannelClosedException(string? message) : base(message) { } + public ChannelClosedException(string? message) : base(message ?? SR.ChannelClosedException_DefaultMessage) { } /// Initializes a new instance of the class. /// The exception that is the cause of this exception. @@ -22,6 +22,6 @@ public ChannelClosedException(Exception? innerException) : /// Initializes a new instance of the class. /// The message that describes the error. /// The exception that is the cause of this exception. - public ChannelClosedException(string? message, Exception? innerException) : base(message, innerException) { } + public ChannelClosedException(string? message, Exception? innerException) : base(message ?? SR.ChannelClosedException_DefaultMessage, innerException) { } } } diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionException.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionException.cs index cb850655b69b04..eee3545ae7ebe6 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionException.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/TransactionException.cs @@ -181,7 +181,7 @@ public TransactionAbortedException() : base(SR.TransactionAborted) /// /// /// - public TransactionAbortedException(string? message) : base(message) + public TransactionAbortedException(string? message) : base(message ?? SR.TransactionAborted) { } @@ -190,7 +190,7 @@ public TransactionAbortedException(string? message) : base(message) /// /// /// - public TransactionAbortedException(string? message, Exception? innerException) : base(message, innerException) + public TransactionAbortedException(string? message, Exception? innerException) : base(message ?? SR.TransactionAborted, innerException) { } @@ -259,7 +259,7 @@ public TransactionInDoubtException() : base(SR.TransactionIndoubt) /// /// /// - public TransactionInDoubtException(string? message) : base(message) + public TransactionInDoubtException(string? message) : base(message ?? SR.TransactionIndoubt) { } @@ -268,7 +268,7 @@ public TransactionInDoubtException(string? message) : base(message) /// /// /// - public TransactionInDoubtException(string? message, Exception? innerException) : base(message, innerException) + public TransactionInDoubtException(string? message, Exception? innerException) : base(message ?? SR.TransactionIndoubt, innerException) { } @@ -318,7 +318,7 @@ public TransactionManagerCommunicationException() : base(SR.TransactionManagerCo /// /// /// - public TransactionManagerCommunicationException(string? message) : base(message) + public TransactionManagerCommunicationException(string? message) : base(message ?? SR.TransactionManagerCommunicationException) { } @@ -330,7 +330,7 @@ public TransactionManagerCommunicationException(string? message) : base(message) public TransactionManagerCommunicationException( string? message, Exception? innerException - ) : base(message, innerException) + ) : base(message ?? SR.TransactionManagerCommunicationException, innerException) { } @@ -361,7 +361,7 @@ public TransactionPromotionException() : this(SR.PromotionFailed) /// /// /// - public TransactionPromotionException(string? message) : base(message) + public TransactionPromotionException(string? message) : base(message ?? SR.PromotionFailed) { } @@ -370,7 +370,7 @@ public TransactionPromotionException(string? message) : base(message) /// /// /// - public TransactionPromotionException(string? message, Exception? innerException) : base(message, innerException) + public TransactionPromotionException(string? message, Exception? innerException) : base(message ?? SR.PromotionFailed, innerException) { } From ee9426a4c2b43055ee43923d621c98e0f84fb5ed Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Wed, 17 Jan 2024 19:09:13 -0800 Subject: [PATCH 090/189] [NativeAOT] Fix Lock's usage of NativeRuntimeEventSource.Log to account for recursive accesses during its own class construction (#94873) * Fix Lock's usage of NativeRuntimeEventSource.Log to account for recursive accesses during its own class construction - `NativeRuntimeEventSource.Log` can be null if it's being constructed in the same thread earlier in the stack, added null checks Fixes https://github.com/dotnet/runtime/issues/94728 --- .../src/System/Threading/Lock.NativeAot.cs | 2 +- .../System.Private.CoreLib/src/System/Threading/Lock.cs | 8 ++++---- .../src/System/Threading/WaitHandle.cs | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.NativeAot.cs index 492e186b95bfc7..318a8cc768024e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Lock.NativeAot.cs @@ -173,7 +173,7 @@ private static bool TryInitializeStatics() s_minSpinCount = DetermineMinSpinCount(); // Also initialize some types that are used later to prevent potential class construction cycles - NativeRuntimeEventSource.Log.IsEnabled(); + _ = NativeRuntimeEventSource.Log; } catch { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs index fe794fdf9426e1..056ff96d41b1ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs @@ -444,9 +444,9 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) Wait: bool areContentionEventsEnabled = - NativeRuntimeEventSource.Log.IsEnabled( + NativeRuntimeEventSource.Log?.IsEnabled( EventLevel.Informational, - NativeRuntimeEventSource.Keywords.ContentionKeyword); + NativeRuntimeEventSource.Keywords.ContentionKeyword) ?? false; AutoResetEvent waitEvent = _waitEvent ?? CreateWaitEvent(areContentionEventsEnabled); if (State.TryLockBeforeWait(this)) { @@ -463,7 +463,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) long waitStartTimeTicks = 0; if (areContentionEventsEnabled) { - NativeRuntimeEventSource.Log.ContentionStart(this); + NativeRuntimeEventSource.Log!.ContentionStart(this); waitStartTimeTicks = Stopwatch.GetTimestamp(); } @@ -535,7 +535,7 @@ private ThreadId TryEnterSlow(int timeoutMs, ThreadId currentThreadId) { double waitDurationNs = (Stopwatch.GetTimestamp() - waitStartTimeTicks) * 1_000_000_000.0 / Stopwatch.Frequency; - NativeRuntimeEventSource.Log.ContentionStop(waitDurationNs); + NativeRuntimeEventSource.Log!.ContentionStop(waitDurationNs); } return currentThreadId; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs index 7d78f3aea97cb7..58b5d8341414b3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @@ -132,6 +132,12 @@ internal bool WaitOneNoCheck( #if !CORECLR // CoreCLR sends the wait events from the native side bool sendWaitEvents = millisecondsTimeout != 0 && +#if NATIVEAOT + // A null check is necessary in NativeAOT due to the possibility of reentrance during class + // construction, as this path can be reached through Lock. See + // https://github.com/dotnet/runtime/issues/94728 for a call stack. + NativeRuntimeEventSource.Log != null && +#endif NativeRuntimeEventSource.Log.IsEnabled( EventLevel.Verbose, NativeRuntimeEventSource.Keywords.WaitHandleKeyword); From 29a35b63b22a041a8e59c84b01b30b5409384ef1 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 17 Jan 2024 22:39:06 -0500 Subject: [PATCH 091/189] Simplify regex code gen for whitespace search (#97078) * Simplify regex code gen for whitespace search Rather than emitting a custom helper, we can just use IndexOfAny{Except} with a SearchValues for the whitespace characters. * Use a nicer field name for the known whitespace search values --- .../gen/RegexGenerator.Emitter.cs | 97 ++++++++++--------- .../Text/RegularExpressions/RegexCharClass.cs | 39 ++++++++ .../Text/RegularExpressions/RegexCompiler.cs | 8 ++ 3 files changed, 99 insertions(+), 45 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs index 4514dc52c865fd..27dc17ff9d800d 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs @@ -400,55 +400,57 @@ private static string EmitSearchValuesOrLiteral(ReadOnlySpan chars, Dictio } /// Adds a SearchValues instance declaration to the required helpers collection. - private static string EmitSearchValues(char[] chars, Dictionary requiredHelpers) + private static string EmitSearchValues(char[] chars, Dictionary requiredHelpers, string? fieldName = null) { Array.Sort(chars); - string fieldName; - if (RegexCharClass.IsAscii(chars)) + if (fieldName is null) { - // The set of ASCII characters can be represented as a 128-bit bitmap. Use the 16-byte hex string as the key. - var bitmap = new byte[16]; - foreach (char c in chars) - { - bitmap[c >> 3] |= (byte)(1 << (c & 7)); - } - - string hexBitmap = BitConverter.ToString(bitmap).Replace("-", string.Empty); - - fieldName = hexBitmap switch - { - "FFFFFFFF000000000000000000000080" => "s_asciiControl", - "000000000000FF030000000000000000" => "s_asciiDigits", - "0000000000000000FEFFFF07FEFFFF07" => "s_asciiLetters", - "000000000000FF03FEFFFF07FEFFFF07" => "s_asciiLettersAndDigits", - "000000000000FF037E0000007E000000" => "s_asciiHexDigits", - "000000000000FF03000000007E000000" => "s_asciiHexDigitsLower", - "000000000000FF037E00000000000000" => "s_asciiHexDigitsUpper", - "00000000EEF7008C010000B800000028" => "s_asciiPunctuation", - "00000000010000000000000000000000" => "s_asciiSeparators", - "00000000100800700000004001000050" => "s_asciiSymbols", - "003E0000010000000000000000000000" => "s_asciiWhiteSpace", - "000000000000FF03FEFFFF87FEFFFF07" => "s_asciiWordChars", - - "00000000FFFFFFFFFFFFFFFFFFFFFF7F" => "s_asciiExceptControl", - "FFFFFFFFFFFF00FCFFFFFFFFFFFFFFFF" => "s_asciiExceptDigits", - "FFFFFFFFFFFFFFFF010000F8010000F8" => "s_asciiExceptLetters", - "FFFFFFFFFFFF00FC010000F8010000F8" => "s_asciiExceptLettersAndDigits", - "FFFFFFFFFFFFFFFFFFFFFFFF010000F8" => "s_asciiExceptLower", - "FFFFFFFF1108FF73FEFFFF47FFFFFFD7" => "s_asciiExceptPunctuation", - "FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFF" => "s_asciiExceptSeparators", - "FFFFFFFFEFF7FF8FFFFFFFBFFEFFFFAF" => "s_asciiExceptSymbols", - "FFFFFFFFFFFFFFFF010000F8FFFFFFFF" => "s_asciiExceptUpper", - "FFC1FFFFFEFFFFFFFFFFFFFFFFFFFFFF" => "s_asciiExceptWhiteSpace", - "FFFFFFFFFFFF00FC01000078010000F8" => "s_asciiExceptWordChars", - - _ => $"s_ascii_{hexBitmap.TrimStart('0')}" - }; - } - else - { - fieldName = GetSHA256FieldName("s_nonAscii_", new string(chars)); + if (RegexCharClass.IsAscii(chars)) + { + // The set of ASCII characters can be represented as a 128-bit bitmap. Use the 16-byte hex string as the key. + var bitmap = new byte[16]; + foreach (char c in chars) + { + bitmap[c >> 3] |= (byte)(1 << (c & 7)); + } + + string hexBitmap = BitConverter.ToString(bitmap).Replace("-", string.Empty); + + fieldName = hexBitmap switch + { + "FFFFFFFF000000000000000000000080" => "s_asciiControl", + "000000000000FF030000000000000000" => "s_asciiDigits", + "0000000000000000FEFFFF07FEFFFF07" => "s_asciiLetters", + "000000000000FF03FEFFFF07FEFFFF07" => "s_asciiLettersAndDigits", + "000000000000FF037E0000007E000000" => "s_asciiHexDigits", + "000000000000FF03000000007E000000" => "s_asciiHexDigitsLower", + "000000000000FF037E00000000000000" => "s_asciiHexDigitsUpper", + "00000000EEF7008C010000B800000028" => "s_asciiPunctuation", + "00000000010000000000000000000000" => "s_asciiSeparators", + "00000000100800700000004001000050" => "s_asciiSymbols", + "003E0000010000000000000000000000" => "s_asciiWhiteSpace", + "000000000000FF03FEFFFF87FEFFFF07" => "s_asciiWordChars", + + "00000000FFFFFFFFFFFFFFFFFFFFFF7F" => "s_asciiExceptControl", + "FFFFFFFFFFFF00FCFFFFFFFFFFFFFFFF" => "s_asciiExceptDigits", + "FFFFFFFFFFFFFFFF010000F8010000F8" => "s_asciiExceptLetters", + "FFFFFFFFFFFF00FC010000F8010000F8" => "s_asciiExceptLettersAndDigits", + "FFFFFFFFFFFFFFFFFFFFFFFF010000F8" => "s_asciiExceptLower", + "FFFFFFFF1108FF73FEFFFF47FFFFFFD7" => "s_asciiExceptPunctuation", + "FFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFF" => "s_asciiExceptSeparators", + "FFFFFFFFEFF7FF8FFFFFFFBFFEFFFFAF" => "s_asciiExceptSymbols", + "FFFFFFFFFFFFFFFF010000F8FFFFFFFF" => "s_asciiExceptUpper", + "FFC1FFFFFEFFFFFFFFFFFFFFFFFFFFFF" => "s_asciiExceptWhiteSpace", + "FFFFFFFFFFFF00FC01000078010000F8" => "s_asciiExceptWordChars", + + _ => $"s_ascii_{hexBitmap.TrimStart('0')}" + }; + } + else + { + fieldName = GetSHA256FieldName("s_nonAscii_", new string(chars)); + } } if (!requiredHelpers.ContainsKey(fieldName)) @@ -1143,6 +1145,11 @@ void EmitFixedSet_LeftToRight() (true, true) => $"{span}.IndexOfAnyExcept({Literal(primarySet.Range.Value.LowInclusive)})", }; } + else if (RegexCharClass.IsUnicodeCategoryOfSmallCharCount(primarySet.Set, out char[]? setChars, out bool negated, out string? description)) + { + // We have a known set of characters, and we can use the supplied IndexOfAny{Except}(...). + indexOf = $"{span}.{(negated ? "IndexOfAnyExcept" : "IndexOfAny")}({EmitSearchValues(setChars, requiredHelpers, $"s_{description}")})"; + } else { // We have an arbitrary set of characters that's really large or otherwise not enumerable. diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs index 2c77add7d7dcab..c56ad4b5b6e054 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs @@ -304,6 +304,13 @@ internal sealed partial class RegexCharClass +"\u3041\u3097\u3099\u30A0\u30A1\u30FB\u30FC\u3100\u3105\u312D\u3131\u318F\u3190\u31B8\u31F0\u321D\u3220\u3244\u3251\u327C\u327F\u32CC\u32D0\u32FF\u3300\u3377\u337B\u33DE\u33E0\u33FF\u3400\u4DB6\u4E00\u9FA6\uA000\uA48D\uA490\uA4C7\uAC00\uD7A4\uF900\uFA2E\uFA30\uFA6B\uFB00\uFB07\uFB13\uFB18\uFB1D\uFB37\uFB38\uFB3D\uFB3E\uFB3F\uFB40\uFB42\uFB43\uFB45\uFB46\uFBB2\uFBD3\uFD3E\uFD50\uFD90\uFD92\uFDC8\uFDF0\uFDFD\uFE00\uFE10\uFE20\uFE24\uFE62\uFE63\uFE64\uFE67\uFE69\uFE6A\uFE70\uFE75\uFE76\uFEFD\uFF04\uFF05\uFF0B\uFF0C\uFF10\uFF1A\uFF1C\uFF1F\uFF21\uFF3B\uFF3E\uFF3F\uFF40\uFF5B\uFF5C\uFF5D\uFF5E\uFF5F\uFF66\uFFBF\uFFC2\uFFC8\uFFCA\uFFD0\uFFD2\uFFD8\uFFDA\uFFDD\uFFE0\uFFE7\uFFE8\uFFEF\uFFFC\uFFFE"], ]; + private static readonly char[] s_whitespaceChars = + ['\u0009', '\u000A', '\u000B', '\u000C', '\u000D', + '\u0020', '\u0085', '\u00A0', '\u1680', '\u2000', + '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', + '\u2006', '\u2007', '\u2008', '\u2009', '\u200A', + '\u2028', '\u2029', '\u202F', '\u205F', '\u3000']; + private List<(char First, char Last)>? _rangelist; private StringBuilder? _categories; private RegexCharClass? _subtractor; @@ -323,6 +330,12 @@ static RegexCharClass() for (int i = 0; i < len - 1; i++) Debug.Assert(string.Compare(s_propTable[i][0], s_propTable[i + 1][0], StringComparison.Ordinal) < 0, $"RegexCharClass s_propTable is out of order at ({s_propTable[i][0]}, {s_propTable[i + 1][0]})"); + // Make sure character information is in sync with Unicode data. + var whitespaceSet = new HashSet(s_whitespaceChars); + for (int i = 0; i <= char.MaxValue; i++) + { + Debug.Assert(whitespaceSet.Contains((char)i) == char.IsWhiteSpace((char)i)); + } } #endif @@ -945,6 +958,32 @@ static bool MayOverlapByEnumeration(string set1, string set2) } } + /// + /// Gets whether the specified set is a named set with a reasonably small count + /// of Unicode characters. + /// + /// The set description. + /// The chars that make up the known set. + /// Whether the need to be negated. + /// A description suitable for use in C# code as a variable name. + public static bool IsUnicodeCategoryOfSmallCharCount(string set, [NotNullWhen(true)] out char[]? chars, out bool negated, [NotNullWhen(true)] out string? description) + { + switch (set) + { + case SpaceClass: + case NotSpaceClass: + chars = s_whitespaceChars; + negated = set == NotSpaceClass; + description = "whitespace"; + return true; + } + + chars = default; + negated = false; + description = null; + return false; + } + /// Gets whether the specified character participates in case conversion. /// /// This method is used to perform operations as if they were case-sensitive even if they're diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index a1547f0bb340d0..45fb0e8a288821 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -953,6 +953,14 @@ void EmitFixedSet_LeftToRight() Call(primarySet.Negated ? s_spanIndexOfAnyExceptInRange : s_spanIndexOfAnyInRange); } } + else if (RegexCharClass.IsUnicodeCategoryOfSmallCharCount(primarySet.Set, out char[]? setChars, out bool negated, out _)) + { + // We have a known set of small number of characters; we can use IndexOfAny{Except}(searchValues). + + // tmp = ...IndexOfAny(s_searchValues); + LoadSearchValues(setChars); + Call(negated ? s_spanIndexOfAnyExceptSearchValues : s_spanIndexOfAnySearchValues); + } else { // In order to optimize the search for ASCII characters, we use SearchValues to vectorize a search From 957ab2f239fa1c425079cdb9eeb483380241dedb Mon Sep 17 00:00:00 2001 From: neon-sunset Date: Thu, 18 Jan 2024 05:40:02 +0200 Subject: [PATCH 092/189] Use .TryGetSpan on sequences instead of type checks to forward sequence comparison for arrays and lists (#97004) --- src/libraries/System.Linq/src/System/Linq/SequenceEqual.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Linq/src/System/Linq/SequenceEqual.cs b/src/libraries/System.Linq/src/System/Linq/SequenceEqual.cs index d1d6dc1586b0f5..ff4c32200a5c34 100644 --- a/src/libraries/System.Linq/src/System/Linq/SequenceEqual.cs +++ b/src/libraries/System.Linq/src/System/Linq/SequenceEqual.cs @@ -24,9 +24,9 @@ public static bool SequenceEqual(this IEnumerable first, IEnum if (first is ICollection firstCol && second is ICollection secondCol) { - if (first is TSource[] firstArray && second is TSource[] secondArray) + if (first.TryGetSpan(out ReadOnlySpan firstSpan) && second.TryGetSpan(out ReadOnlySpan secondSpan)) { - return ((ReadOnlySpan)firstArray).SequenceEqual(secondArray, comparer); + return firstSpan.SequenceEqual(secondSpan, comparer); } if (firstCol.Count != secondCol.Count) From 77d643d8678d0cae1144557f106bb693a1cbca25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 18 Jan 2024 16:39:25 +0900 Subject: [PATCH 093/189] Dead code elimination for `if (typeof(T).IsValueType)` (#97080) @stephentoub found out that for following code: ```csharp using System.Buffers; Foo(); static T[] Foo() { if (typeof(T).IsValueType) { return ArrayPool.Shared.Rent(42); } return null!; } class Bar {} ``` We end up generating `ArrayPool`s of `Bar` even though it's obviously never reachable. The problem is architectural: * We run a whole program analysis phase that tries to figure out things like generic dictionary layouts so that later, in code generation phase, we can inline offsets into generic dictionaries into codegen. * For the above code, whole program analysis decides that the dictionary layout of `Foo<__Canon>` needs a slot for `ArrayPool`. * Codegen then optimizes out the `IsValueType` branch because `__Canon` is never a valuetype. But we already allocated the dictionary slot and will fill it out, even though it ends up unused due to the optimization. We're going to run into issues like this until #83021 is addressed. Whole program analysis cannot currently assume a certain optimization happens because we don't know whether RyuJIT will do it. The only way we can "optimize" during whole program analysis is if we rewrite IL and give RyuJIT no saying in whether to do an optimization or not. Rewriting the IL is not great because it e.g. causes PGO data to not match. I don't like doing it, but there's nothing else we can do. This change extends dead block elimination to understand `typeof(X).IsValueType`. If we recognize a branch is reachable under this condition, we evaluate whether this is true or false and replace the basic block with nops. --- .../Compiler/FeatureSwitchManager.cs | 85 ++++++++++++++----- .../TrimmingBehaviors/DeadCodeElimination.cs | 26 ++++++ 2 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs index 0aa28d85d513c2..600f24e82c950d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/FeatureSwitchManager.cs @@ -591,6 +591,13 @@ private bool TryGetConstantArgument(MethodIL methodIL, byte[] body, OpcodeFlags[ { return true; } + else if (method.IsIntrinsic && method.Name is "get_IsValueType" + && method.OwningType is MetadataType mdt + && mdt.Name == "Type" && mdt.Namespace == "System" && mdt.Module == mdt.Context.SystemModule + && TryExpandTypeIsValueType(methodIL, body, flags, currentOffset, out constant)) + { + return true; + } else { constant = 0; @@ -745,6 +752,40 @@ private string GetResourceStringForAccessor(EcmaMethod method) return null; } + private static bool TryExpandTypeIsValueType(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, out int constant) + { + // We expect to see a sequence: + // ldtoken Foo + // call GetTypeFromHandle + // -> offset points here + constant = 0; + const int SequenceLength = 10; + if (offset < SequenceLength) + return false; + + if ((flags[offset - SequenceLength] & OpcodeFlags.InstructionStart) == 0) + return false; + + ILReader reader = new ILReader(body, offset - SequenceLength); + + TypeDesc type = ReadLdToken(ref reader, methodIL, flags); + if (type == null) + return false; + + if (!ReadGetTypeFromHandle(ref reader, methodIL, flags)) + return false; + + // Dataflow runs on top of uninstantiated IL and we can't answer some questions there. + // Unfortunately this means dataflow will still see code that the rest of the system + // might have optimized away. It should not be a problem in practice. + if (type.IsSignatureVariable) + return false; + + constant = type.IsValueType ? 1 : 0; + + return true; + } + private static bool TryExpandTypeEquality(MethodIL methodIL, byte[] body, OpcodeFlags[] flags, int offset, string op, out int constant) { // We expect to see a sequence: @@ -793,37 +834,37 @@ private static bool TryExpandTypeEquality(MethodIL methodIL, byte[] body, Opcode constant ^= 1; return true; + } - static TypeDesc ReadLdToken(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) - { - ILOpcode opcode = reader.ReadILOpcode(); - if (opcode != ILOpcode.ldtoken) - return null; + private static TypeDesc ReadLdToken(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) + { + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode != ILOpcode.ldtoken) + return null; - TypeDesc t = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); + TypeDesc t = (TypeDesc)methodIL.GetObject(reader.ReadILToken()); - if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) - return null; + if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) + return null; - return t; - } + return t; + } - static bool ReadGetTypeFromHandle(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) - { - ILOpcode opcode = reader.ReadILOpcode(); - if (opcode != ILOpcode.call) - return false; + private static bool ReadGetTypeFromHandle(ref ILReader reader, MethodIL methodIL, OpcodeFlags[] flags) + { + ILOpcode opcode = reader.ReadILOpcode(); + if (opcode != ILOpcode.call) + return false; - MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); + MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken()); - if (!method.IsIntrinsic || method.Name != "GetTypeFromHandle") - return false; + if (!method.IsIntrinsic || method.Name != "GetTypeFromHandle") + return false; - if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) - return false; + if ((flags[reader.Offset] & OpcodeFlags.BasicBlockStart) != 0) + return false; - return true; - } + return true; } private sealed class SubstitutedMethodIL : MethodIL diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index 858ecbbbe48040..21cc8c1fb63c56 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -21,6 +21,7 @@ public static int Run() TestArrayElementTypeOperations.Run(); TestStaticVirtualMethodOptimizations.Run(); TestTypeEquals.Run(); + TestTypeIsValueType.Run(); TestBranchesInGenericCodeRemoval.Run(); TestUnmodifiableStaticFieldOptimization.Run(); TestUnmodifiableInstanceFieldOptimization.Run(); @@ -355,6 +356,31 @@ public static void Run() } } + class TestTypeIsValueType + { + class Never { } + + class Ever { } + + static void Generic() + { + if (typeof(T).IsValueType) + { + Activator.CreateInstance(typeof(Never)); + } + + Activator.CreateInstance(typeof(Ever)); + } + + public static void Run() + { + Generic(); + + ThrowIfPresent(typeof(TestTypeIsValueType), nameof(Never)); + ThrowIfNotPresent(typeof(TestTypeIsValueType), nameof(Ever)); + } + } + class TestBranchesInGenericCodeRemoval { class ClassWithUnusedVirtual From 13ab6eb58e1c16856cfd3bff2fe69da13d6642af Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 18 Jan 2024 13:12:13 +0100 Subject: [PATCH 094/189] JIT: Port loop compaction to new loop representation (#96995) Some diffs expected from (at least) three sources: - Less/different compaction in cases where new loop recognition recognizes fewer loops than old recognition - Less compaction in cases where compacting the first loop led to old loop recognition recognizing another loop after the first one. This leads to no `BBF_OLD_LOOP_HEADER_QUIRK` set on the second loop's header found by new loop finding, meaning we do not compact it. It will be compacted as part of a future PR that enables compaction for all loops. - Slight differences in compaction behavior for blocks that aren't part of the loop and that couldn't be moved, which would previously abort compaction and recognition of the loop. Now we just leave those blocks in place. --- src/coreclr/jit/compiler.h | 7 +- src/coreclr/jit/optimizer.cpp | 834 +++++++++++++--------------------- 2 files changed, 316 insertions(+), 525 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 411a14976907bf..2d15aab8a96560 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6814,7 +6814,12 @@ class Compiler void optFindLoops(); void optFindNewLoops(); - bool optCanonicalizeLoops(FlowGraphNaturalLoops* loops); + bool optCanonicalizeLoops(); + bool optCompactLoops(); + bool optCompactLoop(FlowGraphNaturalLoop* loop); + BasicBlock* optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* top); + BasicBlock* optTryAdvanceLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* insertionPoint, BasicBlock* top, BasicBlock* bottom); + bool optLoopCompactionFixupFallThrough(BasicBlock* block, BasicBlock* oldNext, BasicBlock* newNext); bool optCreatePreheader(FlowGraphNaturalLoop* loop); void optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* preheader); diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index e26af00a4a5048..a0f40efa975251 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -1746,15 +1746,6 @@ class LoopSearch return false; } - // Compact the loop (sweep through it and move out any blocks that aren't part of the - // flow cycle), and find the exits. - if (!MakeCompactAndFindExits()) - { - // Unable to preserve well-formed loop during compaction. - JITDUMP(" can't compact\n"); - return false; - } - // We have a valid loop. return true; } @@ -1949,516 +1940,6 @@ class LoopSearch } return block->bbNum; } - - //------------------------------------------------------------------------ - // MakeCompactAndFindExits: Compact the loop (sweep through it and move out - // any blocks that aren't part of the flow cycle), and find the exits (set - // lastExit and exitCount). - // - // Return Value: - // true - Loop successfully compacted (or `loopBlocks` expanded to - // include all blocks in the lexical range), exits enumerated. - // false - Loop cannot be made compact and remain well-formed. - // - bool MakeCompactAndFindExits() - { - // Compaction (if it needs to happen) will require an insertion point. - BasicBlock* moveAfter = nullptr; - - for (BasicBlock* previous = top->Prev(); previous != bottom;) - { - BasicBlock* block = previous->Next(); - - if (loopBlocks.IsMember(block->bbNum)) - { - // This block is a member of the loop. Check to see if it may exit the loop. - CheckForExit(block); - - // Done processing this block; move on to the next. - previous = block; - continue; - } - - // This block is lexically between TOP and BOTTOM, but it does not - // participate in the flow cycle. Check for a run of consecutive - // such blocks. - // - // If blocks have been reordered and bbNum no longer reflects bbNext ordering - // (say by a call to MakeCompactAndFindExits for an earlier loop or unsuccessful - // attempt to find a loop), the bottom block of this loop may now appear earlier - // in the bbNext chain than other loop blocks. So when the previous hasn't reached bottom - // and block is a non-loop block, and we walk the bbNext chain, we may reach the end. - // If so, give up on recognition of this loop. - // - BasicBlock* lastNonLoopBlock = block; - BasicBlock* nextLoopBlock = block->Next(); - while ((nextLoopBlock != nullptr) && !loopBlocks.IsMember(nextLoopBlock->bbNum)) - { - lastNonLoopBlock = nextLoopBlock; - nextLoopBlock = nextLoopBlock->Next(); - } - - if (nextLoopBlock == nullptr) - { - JITDUMP("Did not find expected loop block when walking from " FMT_BB "\n", lastNonLoopBlock->bbNum); - return false; - } - - // Choose an insertion point for non-loop blocks if we haven't yet done so. - if (moveAfter == nullptr) - { - moveAfter = FindInsertionPoint(); - } - - if (!BasicBlock::sameEHRegion(previous, nextLoopBlock) || !BasicBlock::sameEHRegion(previous, moveAfter)) - { - // EH regions would be ill-formed if we moved these blocks out. - // See if we can consider them loop blocks without introducing - // a side-entry. - if (CanTreatAsLoopBlocks(block, lastNonLoopBlock)) - { - // The call to `canTreatAsLoop` marked these blocks as part of the loop; - // iterate without updating `previous` so that we'll analyze them as part - // of the loop. - continue; - } - else - { - // We can't move these out of the loop or leave them in, so just give - // up on this loop. - return false; - } - } - - // Now physically move the blocks. - BasicBlock* moveBefore = moveAfter->Next(); - - comp->fgUnlinkRange(block, lastNonLoopBlock); - comp->fgMoveBlocksAfter(block, lastNonLoopBlock, moveAfter); - comp->ehUpdateLastBlocks(moveAfter, lastNonLoopBlock); - - // Apply any adjustments needed for fallthrough at the boundaries of the moved region. - FixupFallThrough(moveAfter, moveBefore, block); - FixupFallThrough(lastNonLoopBlock, nextLoopBlock, moveBefore); - // Also apply any adjustments needed where the blocks were snipped out of the loop. - BasicBlock* newBlock = FixupFallThrough(previous, block, nextLoopBlock); - if (newBlock != nullptr) - { - // This new block is in the loop and is a loop exit. - loopBlocks.Insert(newBlock->bbNum); - lastExit = newBlock; - ++exitCount; - } - - // Update moveAfter for the next insertion. - moveAfter = lastNonLoopBlock; - - // Note that we've changed the flow graph, and continue without updating - // `previous` so that we'll process nextLoopBlock. - changedFlowGraph = true; - } - - if ((exitCount == 1) && (lastExit == nullptr)) - { - // If we happen to have a loop with two exits, one of which goes to an - // infinite loop that's lexically nested inside it, where the inner loop - // can't be moved out, we can end up in this situation (because - // CanTreatAsLoopBlocks will have decremented the count expecting to find - // another exit later). Bump the exit count to 2, since downstream code - // will not be prepared for null lastExit with exitCount of 1. - assert(forgotExit); - exitCount = 2; - } - - // Loop compaction was successful - return true; - } - - //------------------------------------------------------------------------ - // FindInsertionPoint: Find an appropriate spot to which blocks that are - // lexically between TOP and BOTTOM but not part of the flow cycle - // can be moved. - // - // Return Value: - // Block after which to insert moved blocks. - // - BasicBlock* FindInsertionPoint() - { - // Find an insertion point for blocks we're going to move. Move them down - // out of the loop, and if possible find a spot that won't break up fall-through. - BasicBlock* moveAfter = bottom; - while (moveAfter->bbFallsThrough()) - { - // Keep looking for a better insertion point if we can. - BasicBlock* newMoveAfter = TryAdvanceInsertionPoint(moveAfter); - - if (newMoveAfter == nullptr) - { - // Ran out of candidate insertion points, so just split up the fall-through. - return moveAfter; - } - - moveAfter = newMoveAfter; - } - - return moveAfter; - } - - //------------------------------------------------------------------------ - // TryAdvanceInsertionPoint: Find the next legal insertion point after - // the given one, if one exists. - // - // Arguments: - // oldMoveAfter - Prior insertion point; find the next after this. - // - // Return Value: - // The next block after `oldMoveAfter` that is a legal insertion point - // (i.e. blocks being swept out of the loop can be moved immediately - // after it), if one exists, else nullptr. - // - BasicBlock* TryAdvanceInsertionPoint(BasicBlock* oldMoveAfter) - { - BasicBlock* newMoveAfter = oldMoveAfter->Next(); - - if (!BasicBlock::sameEHRegion(oldMoveAfter, newMoveAfter)) - { - // Don't cross an EH region boundary. - return nullptr; - } - - if (newMoveAfter->KindIs(BBJ_ALWAYS, BBJ_COND)) - { - unsigned int destNum = newMoveAfter->KindIs(BBJ_ALWAYS) ? newMoveAfter->GetTarget()->bbNum - : newMoveAfter->GetTrueTarget()->bbNum; - if ((destNum >= top->bbNum) && (destNum <= bottom->bbNum) && !loopBlocks.IsMember(destNum)) - { - // Reversing this branch out of block `newMoveAfter` could confuse this algorithm - // (in particular, the edge would still be numerically backwards but no longer be - // lexically backwards, so a lexical forward walk from TOP would not find BOTTOM), - // so don't do that. - // We're checking for BBJ_ALWAYS and BBJ_COND only here -- we don't need to - // check for BBJ_SWITCH because we'd never consider it a loop back-edge. - return nullptr; - } - } - - // Similarly check to see if advancing to `newMoveAfter` would reverse the lexical order - // of an edge from the run of blocks being moved to `newMoveAfter` -- doing so would - // introduce a new lexical back-edge, which could (maybe?) confuse the loop search - // algorithm, and isn't desirable layout anyway. - for (BasicBlock* const predBlock : newMoveAfter->PredBlocks()) - { - unsigned int predNum = predBlock->bbNum; - - if ((predNum >= top->bbNum) && (predNum <= bottom->bbNum) && !loopBlocks.IsMember(predNum)) - { - // Don't make this forward edge a backwards edge. - return nullptr; - } - } - - if (IsRecordedBottom(newMoveAfter)) - { - // This is the BOTTOM of another loop; don't move any blocks past it, to avoid moving them - // out of that loop (we should have already done so when processing that loop if it were legal). - return nullptr; - } - - // Advancing the insertion point is ok, except that we can't split up any call finally - // pair, so if we've got such a pair recurse to see if we can move past the whole thing. - return (newMoveAfter->isBBCallFinallyPair() ? TryAdvanceInsertionPoint(newMoveAfter) : newMoveAfter); - } - - //------------------------------------------------------------------------ - // isOuterBottom: Determine if the given block is the BOTTOM of a previously - // recorded loop. - // - // Arguments: - // block - Block to check for BOTTOM-ness. - // - // Return Value: - // true - The blocks was recorded as `bottom` of some earlier-processed loop. - // false - No loops yet recorded have this block as their `bottom`. - // - bool IsRecordedBottom(BasicBlock* block) - { - if (block->bbNum > oldBlockMaxNum) - { - // This is a new block, which can't be an outer bottom block because we only allow old blocks - // as BOTTOM. - return false; - } - return BlockSetOps::IsMember(comp, bottomBlocks, block->bbNum); - } - - //------------------------------------------------------------------------ - // CanTreatAsLoopBlocks: If the given range of blocks can be treated as - // loop blocks, add them to loopBlockSet and return true. Otherwise, - // return false. - // - // Arguments: - // firstNonLoopBlock - First block in the run to be subsumed. - // lastNonLoopBlock - Last block in the run to be subsumed. - // - // Return Value: - // true - The blocks from `fistNonLoopBlock` to `lastNonLoopBlock` were - // successfully added to `loopBlocks`. - // false - Treating the blocks from `fistNonLoopBlock` to `lastNonLoopBlock` - // would not be legal (it would induce a side-entry). - // - // Notes: - // `loopBlocks` may be modified even if `false` is returned. - // `exitCount` and `lastExit` may be modified if this process identifies - // in-loop edges that were previously counted as exits. - // - bool CanTreatAsLoopBlocks(BasicBlock* firstNonLoopBlock, BasicBlock* lastNonLoopBlock) - { - for (BasicBlock* const testBlock : comp->Blocks(firstNonLoopBlock, lastNonLoopBlock)) - { - for (BasicBlock* const testPred : testBlock->PredBlocks()) - { - unsigned int predPosNum = PositionNum(testPred); - unsigned int firstNonLoopPosNum = PositionNum(firstNonLoopBlock); - unsigned int lastNonLoopPosNum = PositionNum(lastNonLoopBlock); - - if (loopBlocks.IsMember(predPosNum) || - ((predPosNum >= firstNonLoopPosNum) && (predPosNum <= lastNonLoopPosNum))) - { - // This pred is in the loop (or what will be the loop if we determine this - // run of exit blocks doesn't include a side-entry). - - if (predPosNum < firstNonLoopPosNum) - { - // We've already counted this block as an exit, so decrement the count. - --exitCount; - if (lastExit == testPred) - { - // Erase this now-bogus `lastExit` entry. - lastExit = nullptr; - INDEBUG(forgotExit = true); - } - } - } - else - { - // This pred is not in the loop, so this constitutes a side-entry. - return false; - } - } - - // Either we're going to abort the loop on a subsequent testBlock, or this - // testBlock is part of the loop. - loopBlocks.Insert(testBlock->bbNum); - } - - // All blocks were ok to leave in the loop. - return true; - } - - //------------------------------------------------------------------------ - // FixupFallThrough: Re-establish any broken control flow connectivity - // and eliminate any "goto-next"s that were created by changing the - // given block's lexical follower. - // - // Arguments: - // block - Block whose `bbNext` has changed. - // oldNext - Previous value of `block->bbNext`. - // newNext - New value of `block->bbNext`. - // - // Return Value: - // If a new block is created to reconnect flow, the new block is - // returned; otherwise, nullptr. - // - BasicBlock* FixupFallThrough(BasicBlock* block, BasicBlock* oldNext, BasicBlock* newNext) - { - // If we create a new block, that will be our return value. - BasicBlock* newBlock = nullptr; - - if (block->bbFallsThrough()) - { - // Need to reconnect the flow from `block` to `oldNext`. - - if (block->KindIs(BBJ_COND) && block->TrueTargetIs(newNext)) - { - // Reverse the jump condition - GenTree* test = block->lastNode(); - noway_assert(test->OperIsConditionalJump()); - - if (test->OperGet() == GT_JTRUE) - { - GenTree* cond = comp->gtReverseCond(test->AsOp()->gtOp1); - assert(cond == test->AsOp()->gtOp1); // Ensure `gtReverseCond` did not create a new node. - test->AsOp()->gtOp1 = cond; - } - else - { - comp->gtReverseCond(test); - } - - // Redirect the Conditional JUMP to go to `oldNext` - block->SetTrueTarget(oldNext); - block->SetFalseTarget(newNext); - } - else - { - // Insert an unconditional jump to `oldNext` just after `block`. - newBlock = comp->fgConnectFallThrough(block, oldNext); - noway_assert((newBlock == nullptr) || loopBlocks.CanRepresent(newBlock->bbNum)); - } - } - else if (block->KindIs(BBJ_ALWAYS) && block->TargetIs(newNext)) - { - // If block is newNext's only predecessor, move the IR from block to newNext, - // but keep the now-empty block around. - // - // We move the IR because loop recognition has a very limited search capability and - // won't walk from one block's statements to another, even if the blocks form - // a linear chain. So this IR move enhances counted loop recognition. - // - // The logic here is essentially echoing fgCompactBlocks... but we don't call - // that here because we don't want to delete block and do the necessary updates - // to all the other data in flight, and we'd also prefer that newNext be the - // survivor, not block. - // - if ((newNext->bbRefs == 1) && comp->fgCanCompactBlocks(block, newNext)) - { - JITDUMP("Moving stmts from " FMT_BB " to " FMT_BB "\n", block->bbNum, newNext->bbNum); - Statement* stmtList1 = block->firstStmt(); - Statement* stmtList2 = newNext->firstStmt(); - - // Is there anything to move? - // - if (stmtList1 != nullptr) - { - // Append newNext stmts to block's stmts. - // - if (stmtList2 != nullptr) - { - Statement* stmtLast1 = block->lastStmt(); - Statement* stmtLast2 = newNext->lastStmt(); - - stmtLast1->SetNextStmt(stmtList2); - stmtList2->SetPrevStmt(stmtLast1); - stmtList1->SetPrevStmt(stmtLast2); - } - - // Move block's stmts to newNext - // - newNext->bbStmtList = stmtList1; - block->bbStmtList = nullptr; - - // Update newNext's block flags - // - newNext->CopyFlags(block, BBF_COMPACT_UPD); - } - } - } - - return newBlock; - } - - //------------------------------------------------------------------------ - // CheckForExit: Check if the given block has any successor edges that are - // loop exits, and update `lastExit` and `exitCount` if so. - // - // Arguments: - // block - Block whose successor edges are to be checked. - // - // Notes: - // If one block has multiple exiting successor edges, those are counted - // as multiple exits in `exitCount`. - // - void CheckForExit(BasicBlock* block) - { - assert(!block->HasTarget() || block->HasInitializedTarget()); - - switch (block->GetKind()) - { - case BBJ_ALWAYS: - { - BasicBlock* exitPoint = block->GetTarget(); - - if (!loopBlocks.IsMember(exitPoint->bbNum)) - { - // Exit from a block other than BOTTOM - CLANG_FORMAT_COMMENT_ANCHOR; - -#if !defined(FEATURE_EH_FUNCLETS) - // On non-funclet platforms (x86), the catch exit is a BBJ_ALWAYS, but we don't want that to - // be considered a loop exit block, as catch handlers don't have predecessor lists and don't - // show up as might be expected in the dominator tree. - if (!BasicBlock::sameHndRegion(block, exitPoint)) - { - break; - } -#endif // !defined(FEATURE_EH_FUNCLETS) - - lastExit = block; - exitCount++; - } - break; - } - case BBJ_COND: - if (!loopBlocks.IsMember(block->GetTrueTarget()->bbNum)) - { - lastExit = block; - exitCount++; - } - - if (!loopBlocks.IsMember(block->GetFalseTarget()->bbNum)) - { - lastExit = block; - exitCount++; - } - break; - case BBJ_CALLFINALLY: - // Check fallthrough successor - if (!loopBlocks.IsMember(block->Next()->bbNum)) - { - lastExit = block; - exitCount++; - } - - FALLTHROUGH; - case BBJ_CALLFINALLYRET: - case BBJ_EHCATCHRET: - if (!loopBlocks.IsMember(block->GetTarget()->bbNum)) - { - lastExit = block; - exitCount++; - } - break; - - case BBJ_EHFINALLYRET: - case BBJ_EHFAULTRET: - case BBJ_EHFILTERRET: - // The "try" associated with this "finally" must be in the same loop, so the - // finally block will return control inside the loop. - break; - - case BBJ_THROW: - case BBJ_RETURN: - // Those are exits from the loop - lastExit = block; - exitCount++; - break; - - case BBJ_SWITCH: - for (BasicBlock* const exitPoint : block->SwitchTargets()) - { - if (!loopBlocks.IsMember(exitPoint->bbNum)) - { - lastExit = block; - exitCount++; - } - } - break; - - default: - noway_assert(!"Unexpected bbKind"); - break; - } - } }; } // end (anonymous) namespace @@ -4921,15 +4402,22 @@ void Compiler::optFindNewLoops() { m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); - if (optCanonicalizeLoops(m_loops)) + if (optCompactLoops()) + { + fgInvalidateDfsTree(); + m_dfsTree = fgComputeDfs(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + } + + if (optCanonicalizeLoops()) { fgInvalidateDfsTree(); - fgRenumberBlocks(); m_dfsTree = fgComputeDfs(); - m_domTree = FlowGraphDominatorTree::Build(m_dfsTree); m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); } + fgRenumberBlocks(); + // Starting now, we require all loops to have pre-headers. optLoopsRequirePreHeaders = true; @@ -4956,10 +4444,10 @@ void Compiler::optFindNewLoops() // Remarks: // Guarantees that all natural loops have preheaders. // -bool Compiler::optCanonicalizeLoops(FlowGraphNaturalLoops* loops) +bool Compiler::optCanonicalizeLoops() { bool changed = false; - for (FlowGraphNaturalLoop* loop : loops->InReversePostOrder()) + for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) { changed |= optCreatePreheader(loop); } @@ -4967,6 +4455,304 @@ bool Compiler::optCanonicalizeLoops(FlowGraphNaturalLoops* loops) return changed; } +//----------------------------------------------------------------------------- +// optCompactLoops: Compact loops to make their loop blocks lexical if possible. +// +// Returns: +// True if the flow graph was changed. +// +bool Compiler::optCompactLoops() +{ + bool changed = false; + for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) + { + if (!loop->GetHeader()->HasFlag(BBF_OLD_LOOP_HEADER_QUIRK)) + { + continue; + } + + changed |= optCompactLoop(loop); + } + + return changed; +} + +//----------------------------------------------------------------------------- +// optCompactLoop: Compact a specific loop. +// +// Parameters: +// loop - The loop +// +// Returns: +// True if the flow graph was changed. +// +bool Compiler::optCompactLoop(FlowGraphNaturalLoop* loop) +{ + BasicBlock* insertionPoint = nullptr; + + BasicBlock* top = loop->GetLexicallyTopMostBlock(); + unsigned numLoopBlocks = loop->NumLoopBlocks(); + + BasicBlock* cur = top; + bool changedFlowGraph = false; + while (numLoopBlocks > 0) + { + if (loop->ContainsBlock(cur)) + { + numLoopBlocks--; + cur = cur->Next(); + continue; + } + + BasicBlock* lastNonLoopBlock = cur; + while (true) + { + // Should always have a "bottom" block of the loop where we stop. + assert(lastNonLoopBlock->Next() != nullptr); + if (loop->ContainsBlock(lastNonLoopBlock->Next())) + { + break; + } + + lastNonLoopBlock = lastNonLoopBlock->Next(); + } + + if (insertionPoint == nullptr) + { + insertionPoint = optFindLoopCompactionInsertionPoint(loop, top); + } + + BasicBlock* previous = cur->Prev(); + BasicBlock* nextLoopBlock = lastNonLoopBlock->Next(); + assert(previous != nullptr); + if (!BasicBlock::sameEHRegion(previous, nextLoopBlock) || !BasicBlock::sameEHRegion(previous, insertionPoint)) + { + // EH regions would be ill-formed if we moved these blocks out. + cur = nextLoopBlock; + continue; + } + + // Now physically move the blocks. + BasicBlock* moveBefore = insertionPoint->Next(); + + fgUnlinkRange(cur, lastNonLoopBlock); + fgMoveBlocksAfter(cur, lastNonLoopBlock, insertionPoint); + ehUpdateLastBlocks(insertionPoint, lastNonLoopBlock); + + // Apply any adjustments needed for fallthrough at the boundaries of the moved region. + changedFlowGraph |= optLoopCompactionFixupFallThrough(insertionPoint, moveBefore, cur); + changedFlowGraph |= optLoopCompactionFixupFallThrough(lastNonLoopBlock, nextLoopBlock, moveBefore); + // Also apply any adjustments needed where the blocks were snipped out of the loop. + changedFlowGraph |= optLoopCompactionFixupFallThrough(previous, cur, nextLoopBlock); + + // Update insertionPoint for the next insertion. + insertionPoint = lastNonLoopBlock; + + cur = nextLoopBlock; + } + + return changedFlowGraph; +} + +//----------------------------------------------------------------------------- +// optFindLoopCompactionInsertionPoint: Find a good insertion point at which to +// move blocks from the lexical range of "loop" that is not part of the loop. +// +// Parameters: +// loop - The loop +// top - Lexical top block of the loop. +// +// Returns: +// Non-null insertion point. +// +BasicBlock* Compiler::optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* top) +{ + // Find an insertion point for blocks we're going to move. Move them down + // out of the loop, and if possible find a spot that won't break up fall-through. + BasicBlock* bottom = loop->GetLexicallyBottomMostBlock(); + BasicBlock* insertionPoint = bottom; + while (insertionPoint->bbFallsThrough()) + { + // Keep looking for a better insertion point if we can. + BasicBlock* newInsertionPoint = optTryAdvanceLoopCompactionInsertionPoint(loop, insertionPoint, top, bottom); + if (newInsertionPoint == nullptr) + { + // Ran out of candidate insertion points, so just split up the fall-through. + break; + } + + insertionPoint = newInsertionPoint; + } + + return insertionPoint; +} + +//----------------------------------------------------------------------------- +// optTryAdvanceLoopCompactionInsertionPoint: Advance the insertion point to +// avoid having to insert new blocks due to fallthrough. +// +// Parameters: +// loop - The loop +// insertionPoint - Current insertion point +// top - Lexical top block of the loop. +// bottom - Lexical bottom block of the loop. +// +// Returns: +// New insertion point. +// +BasicBlock* Compiler::optTryAdvanceLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, + BasicBlock* insertionPoint, + BasicBlock* top, + BasicBlock* bottom) +{ + BasicBlock* newInsertionPoint = insertionPoint->Next(); + + if (!BasicBlock::sameEHRegion(insertionPoint, newInsertionPoint)) + { + // Don't cross an EH region boundary. + return nullptr; + } + + // TODO-Quirk: Compatibility with old compaction + if (newInsertionPoint->KindIs(BBJ_ALWAYS, BBJ_COND)) + { + BasicBlock* dest = + newInsertionPoint->KindIs(BBJ_ALWAYS) ? newInsertionPoint->GetTarget() : newInsertionPoint->GetTrueTarget(); + if ((dest->bbNum >= top->bbNum) && (dest->bbNum <= bottom->bbNum) && !loop->ContainsBlock(dest)) + { + return nullptr; + } + } + + // TODO-Quirk: Compatibility with old compaction + for (BasicBlock* const predBlock : newInsertionPoint->PredBlocks()) + { + if ((predBlock->bbNum >= top->bbNum) && (predBlock->bbNum <= bottom->bbNum) && !loop->ContainsBlock(predBlock)) + { + // Don't make this forward edge a backwards edge. + return nullptr; + } + } + + // Compaction runs on outer loops before inner loops. That means all + // unlexical blocks here are part of an ancestor loop (or trivial + // BBJ_ALWAYS exit blocks). To avoid breaking lexicality of ancestor loops + // we avoid moving any block past the bottom of an ancestor loop. + for (FlowGraphNaturalLoop* ancestor = loop->GetParent(); ancestor != nullptr; ancestor = ancestor->GetParent()) + { + if (newInsertionPoint == ancestor->GetLexicallyBottomMostBlock()) + { + return nullptr; + } + } + + // Advancing the insertion point is ok, except that we can't split up any call finally + // pair, so if we've got such a pair recurse to see if we can move past the whole thing. + return newInsertionPoint->isBBCallFinallyPair() + ? optTryAdvanceLoopCompactionInsertionPoint(loop, newInsertionPoint, top, bottom) + : newInsertionPoint; +} + +//----------------------------------------------------------------------------- +// optLoopCompactionFixupFallThrough: Fix up fallthrough introduced due to +// moving a range of blocks. +// +// Parameters: +// block - Block that may have fallthrough +// oldNext - The old block that was the fallthrough block +// newNext - The new block that was the fallthrough block +// +// Returns: +// True if the flow graph was changed by this function. +// +bool Compiler::optLoopCompactionFixupFallThrough(BasicBlock* block, BasicBlock* oldNext, BasicBlock* newNext) +{ + bool changed = false; + + if (block->bbFallsThrough()) + { + // Need to reconnect the flow from `block` to `oldNext`. + + if (block->KindIs(BBJ_COND) && (newNext != nullptr) && block->TrueTargetIs(newNext)) + { + // Reverse the jump condition + GenTree* test = block->lastNode(); + noway_assert(test->OperIsConditionalJump()); + + if (test->OperGet() == GT_JTRUE) + { + GenTree* cond = gtReverseCond(test->AsOp()->gtOp1); + assert(cond == test->AsOp()->gtOp1); // Ensure `gtReverseCond` did not create a new node. + test->AsOp()->gtOp1 = cond; + } + else + { + gtReverseCond(test); + } + + // Redirect the Conditional JUMP to go to `oldNext` + block->SetTrueTarget(oldNext); + block->SetFalseTarget(newNext); + } + else + { + // Insert an unconditional jump to `oldNext` just after `block`. + fgConnectFallThrough(block, oldNext); + } + + changed = true; + } + else if (block->KindIs(BBJ_ALWAYS) && block->TargetIs(newNext)) + { + // If block is newNext's only predecessor, move the IR from block to newNext, + // but keep the now-empty block around. + // + // We move the IR because loop recognition has a very limited search capability and + // won't walk from one block's statements to another, even if the blocks form + // a linear chain. So this IR move enhances counted loop recognition. + // + // The logic here is essentially echoing fgCompactBlocks... but we don't call + // that here because we don't want to delete block and do the necessary updates + // to all the other data in flight, and we'd also prefer that newNext be the + // survivor, not block. + // + if ((newNext->bbRefs == 1) && fgCanCompactBlocks(block, newNext)) + { + JITDUMP("Moving stmts from " FMT_BB " to " FMT_BB "\n", block->bbNum, newNext->bbNum); + Statement* stmtList1 = block->firstStmt(); + Statement* stmtList2 = newNext->firstStmt(); + + // Is there anything to move? + // + if (stmtList1 != nullptr) + { + // Append newNext stmts to block's stmts. + // + if (stmtList2 != nullptr) + { + Statement* stmtLast1 = block->lastStmt(); + Statement* stmtLast2 = newNext->lastStmt(); + + stmtLast1->SetNextStmt(stmtList2); + stmtList2->SetPrevStmt(stmtLast1); + stmtList1->SetPrevStmt(stmtLast2); + } + + // Move block's stmts to newNext + // + newNext->bbStmtList = stmtList1; + block->bbStmtList = nullptr; + + // Update newNext's block flags + // + newNext->CopyFlags(block, BBF_COMPACT_UPD); + } + } + } + + return changed; +} + //----------------------------------------------------------------------------- // optCreatePreheader: Create (or find) a preheader for a natural loop. // From 4f1a138c80c5a2cb526240682a5d4d95ddbb4f73 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan <96171496+mkhamoyan@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:21:25 +0100 Subject: [PATCH 095/189] [iOS][non-icu] HybridGlobalization clean up the code (#96974) Clean up the code --- .../features/globalization-hybrid-mode.md | 4 +- .../TestUtilities/System/PlatformDetection.cs | 11 ++- .../Common/tests/Tests/System/StringTests.cs | 32 ++++----- .../tests/CaseInsensitiveComparerTests.cs | 2 +- .../CaseInsensitiveHashCodeProviderTests.cs | 4 +- .../System/Data/SqlTypes/SqlDecimalTest.cs | 2 +- .../Data/SqlTypes/SqlStringSortingTest.cs | 2 +- .../System/Data/SqlTypes/SqlStringTest.cs | 22 +++--- .../GetStringComparerTests.cs | 2 +- .../IdnMapping/Data/Factory.cs | 6 +- .../IdnMapping/IdnMappingGetAsciiTests.cs | 2 +- .../IdnMappingUseStd3AsciiRulesTests.cs | 2 +- .../Normalization/NormalizationAll.cs | 2 +- .../Normalization/StringNormalizationTests.cs | 2 +- .../CompareInfo/CompareInfoTests.Compare.cs | 30 ++++---- .../CompareInfo/CompareInfoTests.IndexOf.cs | 6 +- .../CompareInfo/CompareInfoTests.IsPrefix.cs | 16 ++--- .../CompareInfo/CompareInfoTests.IsSuffix.cs | 14 ++-- .../CompareInfoTests.LastIndexOf.cs | 6 +- .../CompareInfo/CompareInfoTests.cs | 72 +++++++++---------- .../CultureInfo/CultureInfoAll.cs | 2 +- .../CultureInfo/CultureInfoCtor.cs | 8 +-- .../CultureInfo/CultureInfoEnglishName.cs | 2 +- .../CultureInfo/CultureInfoNames.cs | 2 +- .../CultureInfo/CultureInfoNativeName.cs | 2 +- .../CultureInfo/GetCultureInfo.cs | 8 +-- ...DateTimeFormatInfoGetAbbreviatedEraName.cs | 2 +- .../DateTimeFormatInfoGetEra.cs | 2 +- .../DateTimeFormatInfoTests.cs | 2 +- .../IcuAppLocal/IcuAppLocal.cs | 2 +- .../System.Globalization.Tests/IcuTests.cs | 2 +- .../NumberFormatInfo/NumberFormatInfoData.cs | 2 +- .../System/Globalization/RegionInfoTests.cs | 8 +-- .../AssemblyNameTests.cs | 2 +- .../System/Convert.ToDateTime.cs | 2 +- .../System/StringComparer.cs | 2 +- .../System/DateTimeTests.cs | 4 +- .../System/StringGetHashCodeTests.cs | 2 +- .../System/StringTests.cs | 22 +++--- .../System/Text/RuneTests.cs | 2 +- .../System/TimeZoneInfoTests.cs | 4 +- src/mono/CMakeLists.txt | 1 + .../CMakeLists.txt | 1 + .../System.Globalization.Native/entrypoints.c | 2 +- .../pal_calendarData.h | 2 +- .../pal_calendarData.m | 2 +- .../System.Globalization.Native/pal_casing.h | 2 +- .../System.Globalization.Native/pal_casing.m | 2 +- .../pal_collation.h | 2 +- .../pal_collation.m | 2 +- .../pal_icushim_internal.h | 4 +- .../pal_icushim_static.c | 2 +- .../System.Globalization.Native/pal_locale.h | 2 +- .../System.Globalization.Native/pal_locale.m | 2 +- .../pal_localeNumberData.h | 2 +- .../pal_localeStringData.h | 2 +- .../pal_normalization.h | 2 +- .../pal_normalization.m | 2 +- .../pal_timeZoneInfo.h | 2 +- .../pal_timeZoneInfo.m | 2 +- 60 files changed, 181 insertions(+), 180 deletions(-) diff --git a/docs/design/features/globalization-hybrid-mode.md b/docs/design/features/globalization-hybrid-mode.md index fa70e222badffa..9d993f7b31c825 100644 --- a/docs/design/features/globalization-hybrid-mode.md +++ b/docs/design/features/globalization-hybrid-mode.md @@ -364,9 +364,9 @@ The Hybrid responses may differ because they use Web API functions. To better il | ShortTimePattern | `Intl.DateTimeFormat(locale, { timeStyle: "medium" })` | bg-BG | HH:mm | H:mm | | YearMonthPattern | `Date.prototype.toLocaleDateString(locale, { year: "numeric", month: "long" })` | ar-SA | MMMM yyyy | MMMM yyyy g | -### OSX +### Apple platforms -For OSX platforms we are using native apis instead of ICU data. +For Apple platforms (iOS/tvOS/maccatalyst) we are using native apis instead of ICU data. ## String comparison diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index db8416cf5ddd10..68ec070f3b1d56 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -382,17 +382,16 @@ public static string GetDistroVersionString() public static bool IsInvariantGlobalization => m_isInvariant.Value; public static bool IsHybridGlobalization => m_isHybrid.Value; public static bool IsHybridGlobalizationOnBrowser => m_isHybrid.Value && IsBrowser; - public static bool IsHybridGlobalizationOnOSX => m_isHybrid.Value && (IsOSX || IsMacCatalyst || IsiOS || IstvOS); + public static bool IsHybridGlobalizationOnApplePlatform => m_isHybrid.Value && (IsMacCatalyst || IsiOS || IstvOS); public static bool IsNotHybridGlobalizationOnBrowser => !IsHybridGlobalizationOnBrowser; public static bool IsNotInvariantGlobalization => !IsInvariantGlobalization; public static bool IsNotHybridGlobalization => !IsHybridGlobalization; - public static bool IsNotHybridGlobalizationOnOSX => !IsHybridGlobalizationOnOSX; + public static bool IsNotHybridGlobalizationOnApplePlatform => !IsHybridGlobalizationOnApplePlatform; - // HG on apple platforms implies ICU - public static bool IsIcuGlobalization => !IsInvariantGlobalization && (IsHybridGlobalizationOnOSX || ICUVersion > new Version(0, 0, 0, 0)); + // HG on apple platforms implies ICU + public static bool IsIcuGlobalization => !IsInvariantGlobalization && (IsHybridGlobalizationOnApplePlatform || ICUVersion > new Version(0, 0, 0, 0)); public static bool IsIcuGlobalizationAndNotHybridOnBrowser => IsIcuGlobalization && IsNotHybridGlobalizationOnBrowser; - public static bool IsIcuGlobalizationAndNotHybrid => IsIcuGlobalization && IsNotHybridGlobalization; public static bool IsNlsGlobalization => IsNotInvariantGlobalization && !IsIcuGlobalization && !IsHybridGlobalization; public static bool IsSubstAvailable @@ -421,7 +420,7 @@ private static Version GetICUVersion() { int version = 0; // When HG on Apple platforms, our ICU lib is not loaded - if (IsNotHybridGlobalizationOnOSX) + if (IsNotHybridGlobalizationOnApplePlatform) { try { diff --git a/src/libraries/Common/tests/Tests/System/StringTests.cs b/src/libraries/Common/tests/Tests/System/StringTests.cs index a16883f27d67e8..429192f549d672 100644 --- a/src/libraries/Common/tests/Tests/System/StringTests.cs +++ b/src/libraries/Common/tests/Tests/System/StringTests.cs @@ -1009,7 +1009,7 @@ public static void MakeSureNoCompareToChecksGoOutOfRange_StringComparison() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public static void CompareToNoMatch_StringComparison() { for (int length = 1; length < 150; length++) @@ -1284,7 +1284,7 @@ public static void ContainsMatchDifferentSpans_StringComparison() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [ActiveIssue("https://github.com/dotnet/runtime/issues/95471", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] public static void ContainsNoMatch_StringComparison() { @@ -1663,7 +1663,7 @@ public static IEnumerable EndsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.CurrentCulture, true }; yield return new object[] { "", "a", StringComparison.CurrentCulture, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.CurrentCulture, true }; // CurrentCultureIgnoreCase @@ -1675,7 +1675,7 @@ public static IEnumerable EndsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.CurrentCultureIgnoreCase, true }; yield return new object[] { "", "a", StringComparison.CurrentCultureIgnoreCase, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true }; // InvariantCulture @@ -1688,7 +1688,7 @@ public static IEnumerable EndsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.InvariantCulture, true }; yield return new object[] { "", "a", StringComparison.InvariantCulture, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.InvariantCulture, true }; // InvariantCultureIgnoreCase @@ -1700,7 +1700,7 @@ public static IEnumerable EndsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.InvariantCultureIgnoreCase, true }; yield return new object[] { "", "a", StringComparison.InvariantCultureIgnoreCase, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true }; // Ordinal @@ -2113,7 +2113,7 @@ public static void EndsWithMatchDifferentSpans_StringComparison() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public static void EndsWithNoMatch_StringComparison() { for (int length = 1; length < 150; length++) @@ -3199,7 +3199,7 @@ public static void IndexOf_TurkishI_EnglishUSCulture() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform))] [ActiveIssue("https://github.com/dotnet/runtime/issues/60568", TestPlatforms.Android | TestPlatforms.LinuxBionic)] public static void IndexOf_HungarianDoubleCompression_HungarianCulture() { @@ -4856,7 +4856,7 @@ public static IEnumerable StartsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.CurrentCulture, true }; yield return new object[] { "", "hello", StringComparison.CurrentCulture, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { // "https://github.com/dotnet/runtime/issues/95473" if (PlatformDetection.IsNotHybridGlobalizationOnBrowser) @@ -4872,7 +4872,7 @@ public static IEnumerable StartsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.CurrentCultureIgnoreCase, true }; yield return new object[] { "", "hello", StringComparison.CurrentCultureIgnoreCase, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.CurrentCultureIgnoreCase, true }; // InvariantCulture @@ -4884,7 +4884,7 @@ public static IEnumerable StartsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.InvariantCulture, true }; yield return new object[] { "", "hello", StringComparison.InvariantCulture, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.InvariantCulture, true }; // InvariantCultureIgnoreCase @@ -4896,7 +4896,7 @@ public static IEnumerable StartsWith_StringComparison_TestData() yield return new object[] { "", "", StringComparison.InvariantCultureIgnoreCase, true }; yield return new object[] { "", "hello", StringComparison.InvariantCultureIgnoreCase, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.InvariantCultureIgnoreCase, true }; // Ordinal @@ -5354,7 +5354,7 @@ private static IEnumerable ToLower_Culture_TestData() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [ActiveIssue("https://github.com/dotnet/runtime/issues/95503", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] public static void Test_ToLower_Culture() { @@ -5871,7 +5871,7 @@ public static IEnumerable ToUpper_Culture_TestData() } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [MemberData(nameof(ToUpper_Culture_TestData))] [ActiveIssue("https://github.com/dotnet/runtime/issues/95503", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] public static void Test_ToUpper_Culture(string actual, string expected, CultureInfo culture) @@ -5971,7 +5971,7 @@ public static IEnumerable ToUpper_TurkishI_InvariantCulture_MemberData new KeyValuePair('\u0130', '\u0130'), new KeyValuePair('\u0131', '\u0131')); - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform))] [MemberData(nameof(ToUpper_TurkishI_InvariantCulture_MemberData))] [ActiveIssue("https://github.com/dotnet/runtime/issues/95471", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] public static void ToUpper_TurkishI_InvariantCulture(string s, string expected) @@ -7242,7 +7242,7 @@ public static void StartsWithMatchDifferentSpans_StringComparison() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public static void StartsWithNoMatch_StringComparison() { for (int length = 1; length < 150; length++) diff --git a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs index 362a76fc3dc355..81dcc28a9165e7 100644 --- a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs +++ b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs @@ -66,7 +66,7 @@ public void Ctor_CultureInfo_Compare(object a, object b, int expected) [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android | TestPlatforms.LinuxBionic)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Ctor_CultureInfo_Compare_TurkishI() { var cultureNames = Helpers.TestCultureNames; diff --git a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs index 18b2e7421682c6..36683f262c4781 100644 --- a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs +++ b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs @@ -96,7 +96,7 @@ public void Ctor_CultureInfo_ChangeCurrentCulture_GetHashCodeCompare(object a, o [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android | TestPlatforms.LinuxBionic)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Ctor_CultureInfo_GetHashCodeCompare_TurkishI() { var cultureNames = Helpers.TestCultureNames; @@ -153,7 +153,7 @@ public void Default_GetHashCodeCompare(object a, object b, bool expected) [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android | TestPlatforms.LinuxBionic)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Default_Compare_TurkishI() { // Turkish has lower-case and upper-case version of the dotted "i", so the upper case of "i" (U+0069) isn't "I" (U+0049) diff --git a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlDecimalTest.cs b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlDecimalTest.cs index 1f8bdc8ba02539..ba2b279425dd68 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlDecimalTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlDecimalTest.cs @@ -196,7 +196,7 @@ public void AdjustScale() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void ConvertToPrecScale() { Assert.Equal(new SqlDecimal(6464.6m).Value, SqlDecimal.ConvertToPrecScale(_test1, 5, 1).Value); diff --git a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs index d6a17074d8d934..3ac24fb705c54c 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs @@ -38,7 +38,7 @@ public static class SqlStringSortingTest private static readonly UnicodeEncoding s_unicodeEncoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: true); [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [InlineData("ja-JP", 0x0411)] // Japanese - Japan [InlineData("ar-SA", 0x0401)] // Arabic - Saudi Arabia [InlineData("de-DE", 0x0407)] // German - Germany diff --git a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringTest.cs b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringTest.cs index 1b79fdbe0fdd7a..0aa18b8b18f2ae 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringTest.cs @@ -56,7 +56,7 @@ public SqlStringTest() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Constructor_Value_Success() { const string value = "foo"; @@ -220,7 +220,7 @@ public void CompareToSqlTypeException() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void CompareTo() { Assert.True(_test1.CompareTo(_test3) < 0); @@ -261,7 +261,7 @@ public void CompareTo() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void EqualsMethods() { Assert.False(_test1.Equals(_test2)); @@ -278,7 +278,7 @@ public void EqualsMethods() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Greaters() { // GreateThan () @@ -293,7 +293,7 @@ public void Greaters() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Lessers() { // LessThan() @@ -309,7 +309,7 @@ public void Lessers() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void NotEquals() { Assert.True(SqlString.NotEquals(_test1, _test2).Value); @@ -320,7 +320,7 @@ public void NotEquals() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Concat() { _test1 = new SqlString("First TestString"); @@ -332,7 +332,7 @@ public void Concat() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void Clone() { SqlString testSqlString = _test1.Clone(); @@ -352,7 +352,7 @@ public void CompareOptionsFromSqlCompareOptions() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void UnicodeBytes() { Assert.Equal((byte)105, _test1.GetNonUnicodeBytes()[1]); @@ -549,7 +549,7 @@ public void Conversions() // OPERATORS [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void ArithmeticOperators() { SqlString testString = new SqlString("...Testing..."); @@ -558,7 +558,7 @@ public void ArithmeticOperators() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void ThanOrEqualOperators() { // == -operator diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs index 17801793edd566..bab4eaadd8b507 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs @@ -20,7 +20,7 @@ public void GetStringComparer_Invalid() } [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [InlineData("hello", "hello", "fr-FR", CompareOptions.IgnoreCase, 0, 0)] [InlineData("hello", "HELLo", "fr-FR", CompareOptions.IgnoreCase, 0, 0)] [InlineData("hello", null, "fr-FR", CompareOptions.IgnoreCase, 1, 1)] diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/Data/Factory.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/Data/Factory.cs index 05018f6ca7476c..d53f2458580c40 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/Data/Factory.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/Data/Factory.cs @@ -26,7 +26,7 @@ private static string RemoveComment(string line) private static Stream GetIdnaTestTxt() { string fileName = null; - if (PlatformDetection.ICUVersion >= new Version(66, 0) || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.ICUVersion >= new Version(66, 0) || PlatformDetection.IsHybridGlobalizationOnApplePlatform) fileName = "IdnaTest_13.txt"; else if (PlatformDetection.IsWindows7) fileName = "IdnaTest_Win7.txt"; @@ -61,7 +61,7 @@ private static IEnumerable ParseFile(Stream stream, Func= new Version(66, 0) || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.ICUVersion >= new Version(66, 0) || PlatformDetection.IsHybridGlobalizationOnApplePlatform) return new Unicode_13_0_IdnaTest(line, lineCount); else if (PlatformDetection.IsWindows7) return new Unicode_Win7_IdnaTest(line, lineCount); @@ -88,7 +88,7 @@ public static IEnumerable GetDataset() { // Nls is transitional so we filter out non transitional test cases. // Icu is the opposite. - IdnType idnFilter = PlatformDetection.IsNlsGlobalization || PlatformDetection.IsHybridGlobalizationOnOSX ? IdnType.Nontransitional : IdnType.Transitional; + IdnType idnFilter = PlatformDetection.IsNlsGlobalization || PlatformDetection.IsHybridGlobalizationOnApplePlatform ? IdnType.Nontransitional : IdnType.Transitional; foreach (var entry in ParseFile(GetIdnaTestTxt(), GetConformanceIdnaTest)) { if (entry.Type != idnFilter && entry.Source != string.Empty) diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingGetAsciiTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingGetAsciiTests.cs index 3ad3339754359a..595b005c96d918 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingGetAsciiTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingGetAsciiTests.cs @@ -21,7 +21,7 @@ public static IEnumerable GetAscii_TestData() } string ascii = c.ToString(); - if ((!PlatformDetection.IsIcuGlobalization && !PlatformDetection.IsHybridGlobalizationOnOSX) || c != '-') // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 + if (!PlatformDetection.IsIcuGlobalization || c != '-') // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 { yield return new object[] { ascii, 0, 1, ascii }; } diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs index 2839086a0b2f80..481865437c16cb 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs @@ -49,7 +49,7 @@ public void UseStd3AsciiRules_ChangesGetAsciiBehavior(string unicode, bool conta var idnStd3False = new IdnMapping { UseStd3AsciiRules = false }; var idnStd3True = new IdnMapping { UseStd3AsciiRules = true }; - if (containsInvalidHyphen && (PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnOSX)) + if (containsInvalidHyphen && PlatformDetection.IsIcuGlobalization) { // ICU always fails on leading/trailing hyphens regardless of the Std3 rules option. AssertExtensions.Throws("unicode", () => idnStd3False.GetAscii(unicode)); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/NormalizationAll.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/NormalizationAll.cs index 8d27652096c51b..703988c1cffb39 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/NormalizationAll.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/NormalizationAll.cs @@ -52,7 +52,7 @@ public void Normalize() VerifyConformanceInvariant(NormalizationForm.FormD, part0, part1, part2, part3, part4); // Mobile / Browser ICU doesn't support FormKC and FormKD - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { // Form KC VerifyConformanceInvariant(NormalizationForm.FormKC, part0, part1, part2, part3, part4); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/StringNormalizationTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/StringNormalizationTests.cs index 252d19a16f17b8..1b70a79b6ae6de 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/StringNormalizationTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/StringNormalizationTests.cs @@ -49,7 +49,7 @@ public static IEnumerable NormalizeTestData() yield return new object[] { "\u1E9b\u0323", NormalizationForm.FormC, "\u1E9b\u0323" }; yield return new object[] { "\u1E9b\u0323", NormalizationForm.FormD, "\u017f\u0323\u0307" }; - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { // Mobile / Browser ICU doesn't support FormKC and FormKD yield return new object[] { "\uFB01", NormalizationForm.FormKC, "fi" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs index 0a642b36c5694b..484023f579f6ab 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs @@ -62,7 +62,7 @@ public static IEnumerable Compare_TestData() if (!PlatformDetection.IsHybridGlobalizationOnBrowser) { yield return new object[] { s_invariantCompare, "\u3042", "\uFF71", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, 0 }; - yield return new object[] { s_invariantCompare, "'\u3000'", "' '", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : 0 }; + yield return new object[] { s_invariantCompare, "'\u3000'", "' '", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : 0 }; } yield return new object[] { s_invariantCompare, "\u6FA4", "\u6CA2", CompareOptions.None, 1 }; @@ -105,11 +105,11 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "'", "\uFF07", CompareOptions.IgnoreWidth, 0 }; yield return new object[] { s_invariantCompare, "\"", "\uFF02", CompareOptions.IgnoreWidth, 0 }; } - yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : s_expectedHiraganaToKatakanaCompare }; + yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u3042", "\u30A2", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u3042", "\uFF71", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; - yield return new object[] { s_invariantCompare, "\u304D\u3083", "\u30AD\u30E3", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : s_expectedHiraganaToKatakanaCompare }; - yield return new object[] { s_invariantCompare, "\u304D\u3083", "\u30AD\u3083", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : s_expectedHiraganaToKatakanaCompare }; + yield return new object[] { s_invariantCompare, "\u304D\u3083", "\u30AD\u30E3", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : s_expectedHiraganaToKatakanaCompare }; + yield return new object[] { s_invariantCompare, "\u304D\u3083", "\u30AD\u3083", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u304D \u3083", "\u30AD\u3083", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u3044", "I", CompareOptions.None, 1 }; @@ -123,7 +123,7 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "\u3070\u3073\u3076\u3079\u307C", "\u30D0\u30D3\u30D6\u30D9\u30DC", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u3070\u3073\u3076\u3079\u307C", "\u30D0\u30D3\u3076\u30D9\u30DC", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; - yield return new object[] { s_invariantCompare, "\u3070\u3073\uFF8C\uFF9E\uFF8D\uFF9E\u307C", "\u30D0\u30D3\u3076\u30D9\uFF8E\uFF9E", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : s_expectedHiraganaToKatakanaCompare }; + yield return new object[] { s_invariantCompare, "\u3070\u3073\uFF8C\uFF9E\uFF8D\uFF9E\u307C", "\u30D0\u30D3\u3076\u30D9\uFF8E\uFF9E", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u3070\u3073\uFF8C\uFF9E\uFF8D\uFF9E\u307C", "\uFF8E\uFF9E", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u3070\u30DC\uFF8C\uFF9E\uFF8D\uFF9E\u307C", "\u3079\uFF8E\uFF9E", CompareOptions.None, -1 }; @@ -138,7 +138,7 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "ABCDE", "\uFF43D", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "ABCDE", "c", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u3060", "\u305F", CompareOptions.None, 1 }; - yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : s_expectedHiraganaToKatakanaCompare }; + yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u68EE\u9D0E\u5916", "\u68EE\u9DD7\u5916", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u68EE\u9DD7\u5916", "\u68EE\u9DD7\u5916", CompareOptions.None, 0 }; @@ -197,8 +197,8 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "FooBar", "Foo\u0400Bar", CompareOptions.Ordinal, -1 }; yield return new object[] { s_invariantCompare, "FooBA\u0300R", "FooB\u00C0R", supportedIgnoreNonSpaceOption, 0 }; - // In HybridGlobalization on OSX IgnoreSymbols is not supported - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + // In HybridGlobalization on Apple platforms IgnoreSymbols is not supported + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.IgnoreSymbols, 0 }; } @@ -224,8 +224,8 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.IgnoreCase, -1 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.None, -1 }; - // In HybridGlobalization mode on OSX IgnoreSymbols is not supported - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + // In HybridGlobalization mode on Apple platforms IgnoreSymbols is not supported + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.IgnoreSymbols, 0 }; yield return new object[] { s_invariantCompare, "\uFF65", "\u30FB", CompareOptions.IgnoreSymbols, 0 }; @@ -245,8 +245,8 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreWidth, 0 }; } - // In HybridGlobalization mode on OSX IgnoreSymbols is not supported - if(!PlatformDetection.IsHybridGlobalizationOnOSX) + // In HybridGlobalization mode on Apple platforms IgnoreSymbols is not supported + if(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreSymbols, s_expectedHalfToFullFormsComparison }; } @@ -263,7 +263,7 @@ public static IEnumerable Compare_TestData() yield return new object[] { kanaComparison, "\u3060", "\u30C0", CompareOptions.IgnoreKanaType, 0 }; yield return new object[] { kanaComparison, "c", "C", CompareOptions.IgnoreKanaType, -1 }; - yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.IgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : s_expectedHiraganaToKatakanaCompare }; + yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.IgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : s_expectedHiraganaToKatakanaCompare }; // Japanese [semi-]voiced sound mark yield return new object[] { s_invariantCompare, "\u306F", "\u3070", CompareOptions.IgnoreCase, -1 }; @@ -289,7 +289,7 @@ public static IEnumerable Compare_TestData() CompareOptions.IgnoreKanaType | CompareOptions.IgnoreCase : CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase; - yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", japaneseCmp, useNls || PlatformDetection.IsHybridGlobalizationOnOSX ? 1: 0 }; + yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", japaneseCmp, useNls || PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1: 0 }; yield return new object[] { s_invariantCompare, "'\u3000'", "''", japaneseCmp, useNls ? 1 : -1 }; yield return new object[] { s_invariantCompare, "\u30BF", "\uFF80", CompareOptions.None, useNls ? 1 : -1 }; @@ -560,7 +560,7 @@ public static IEnumerable Compare_HiraganaAndKatakana_TestData() CompareOptions.IgnoreKanaType | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase, CompareOptions.Ordinal, CompareOptions.OrdinalIgnoreCase, - } : PlatformDetection.IsHybridGlobalizationOnOSX ? + } : PlatformDetection.IsHybridGlobalizationOnApplePlatform ? new[] { CompareOptions.None, CompareOptions.IgnoreCase, diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs index dea1a24959cbd9..2b89148d2fad20 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs @@ -33,7 +33,7 @@ public static IEnumerable IndexOf_TestData() yield return new object[] { s_invariantCompare, "foobardzsdzs", "rddzs", 0, 12, CompareOptions.Ordinal, -1, 0 }; // Slovak - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_slovakCompare, "ch", "h", 0, 2, CompareOptions.None, -1, 0 }; // Android has its own ICU, which doesn't work well with slovak @@ -82,7 +82,7 @@ public static IEnumerable IndexOf_TestData() yield return new object[] { s_invariantCompare, "hello", "\u200d", 1, 3, CompareOptions.IgnoreCase, 1, 0 }; // Ignore symbols - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { s_invariantCompare, "More Test's", "Tests", 0, 11, CompareOptions.IgnoreSymbols, 5, 6 }; yield return new object[] { s_invariantCompare, "More Test's", "Tests", 0, 11, CompareOptions.None, -1, 0 }; yield return new object[] { s_invariantCompare, "cbabababdbaba", "ab", 0, 13, CompareOptions.None, 2, 2 }; @@ -142,7 +142,7 @@ public static IEnumerable IndexOf_TestData() { yield return new object[] { s_germanCompare, "abc Strasse Strasse xyz", "stra\u00DFe", 0, 23, supportedIgnoreCaseIgnoreNonSpaceOptions, 4, 7 }; yield return new object[] { s_germanCompare, "abc stra\u00DFe stra\u00DFe xyz", "Strasse", 0, 21, supportedIgnoreCaseIgnoreNonSpaceOptions, 4, 6 }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "abcdzxyz", "\u01F3", 0, 8, supportedIgnoreNonSpaceOption, 3, 2 }; yield return new object[] { s_invariantCompare, "abc\u01F3xyz", "dz", 0, 7, supportedIgnoreNonSpaceOption, 3, 1 }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs index d3486a1d841576..f709f1de1348cc 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs @@ -25,7 +25,7 @@ public static IEnumerable IsPrefix_TestData() yield return new object[] { s_invariantCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.Ordinal, false, 0 }; yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.Ordinal, false, 0 }; yield return new object[] { s_invariantCompare, "dz", "d", CompareOptions.None, true, 1 }; - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { s_hungarianCompare, "dz", "d", CompareOptions.None, false, 0 }; yield return new object[] { s_hungarianCompare, "dz", "d", CompareOptions.Ordinal, true, 1 }; @@ -35,7 +35,7 @@ public static IEnumerable IsPrefix_TestData() if (!PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic) { yield return new object[] { s_turkishCompare, "interesting", "I", CompareOptions.IgnoreCase, false, 0 }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { s_turkishCompare, "interesting", "\u0130", CompareOptions.IgnoreCase, true, 1 }; } yield return new object[] { s_turkishCompare, "interesting", "\u0130", CompareOptions.None, false, 0 }; @@ -72,7 +72,7 @@ public static IEnumerable IsPrefix_TestData() yield return new object[] { s_invariantCompare, "\uD800\uD800", "\uD800\uD800", CompareOptions.None, true, 2 }; // Ignore symbols - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.IgnoreSymbols, true, 6 }; yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.None, false, 0 }; @@ -84,7 +84,7 @@ public static IEnumerable IsPrefix_TestData() (PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsBrowserDomSupportedOrNodeJS); if (behavesLikeNls) { - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, true, 7 }; yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, true, 7 }; @@ -96,10 +96,10 @@ public static IEnumerable IsPrefix_TestData() else { yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, false, 0 }; - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, false, 0 }; yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, false, 0 }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.None, false, 0 }; yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.IgnoreCase, false, 0 }; @@ -114,7 +114,7 @@ public static IEnumerable IsPrefix_TestData() } // Prefixes where matched length does not equal value string length - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "dzxyz", "\u01F3", supportedIgnoreNonSpaceOption, true, 2 }; yield return new object[] { s_invariantCompare, "\u01F3xyz", "dz", supportedIgnoreNonSpaceOption, true, 1 }; @@ -151,7 +151,7 @@ public void IsPrefix(CompareInfo compareInfo, string source, string value, Compa valueBoundedMemory.MakeReadonly(); Assert.Equal(expected, compareInfo.IsPrefix(sourceBoundedMemory.Span, valueBoundedMemory.Span, options)); - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { Assert.Equal(expected, compareInfo.IsPrefix(sourceBoundedMemory.Span, valueBoundedMemory.Span, options, out int actualMatchLength)); Assert.Equal(expectedMatchLength, actualMatchLength); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs index 906392ba3fed9e..3b2d048582536e 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs @@ -25,12 +25,12 @@ public static IEnumerable IsSuffix_TestData() yield return new object[] { s_invariantCompare, "foobardzsdzs", "rddzs", CompareOptions.None, false, 0 }; yield return new object[] { s_invariantCompare, "foobardzsdzs", "rddzs", CompareOptions.Ordinal, false, 0 }; yield return new object[] { s_invariantCompare, "dz", "z", CompareOptions.None, true, 1 }; - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { s_hungarianCompare, "dz", "z", CompareOptions.None, false, 0 }; yield return new object[] { s_hungarianCompare, "dz", "z", CompareOptions.Ordinal, true, 1 }; // Slovak - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_slovakCompare, "ch", "h", CompareOptions.None, false, 0 }; yield return new object[] { s_slovakCompare, "velmi chora", "hora", CompareOptions.None, false, 0 }; @@ -80,7 +80,7 @@ public static IEnumerable IsSuffix_TestData() yield return new object[] { s_invariantCompare, "\uD800\uD800", "\uD800\uD800", CompareOptions.None, true, 2 }; // Ignore symbols - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "More Test's", "Tests", CompareOptions.IgnoreSymbols, true, 6 }; yield return new object[] { s_invariantCompare, "More Test's", "Tests", CompareOptions.None, false, 0 }; @@ -107,7 +107,7 @@ public static IEnumerable IsSuffix_TestData() { yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", CompareOptions.None, false, 0 }; yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, false, 0 }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.None, false, 0 }; yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.IgnoreCase, false, 0 }; @@ -116,7 +116,7 @@ public static IEnumerable IsSuffix_TestData() // Suffixes where matched length does not equal value string length yield return new object[] { s_germanCompare, "xyz Strasse", "xtra\u00DFe", supportedIgnoreCaseIgnoreNonSpaceOptions, false, 0 }; - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "xyzdz", "\u01F3", supportedIgnoreNonSpaceOption, true, 2 }; yield return new object[] { s_invariantCompare, "xyz\u01F3", "dz", supportedIgnoreNonSpaceOption, true, 1 }; @@ -152,7 +152,7 @@ public void IsSuffix(CompareInfo compareInfo, string source, string value, Compa valueBoundedMemory.MakeReadonly(); Assert.Equal(expected, compareInfo.IsSuffix(sourceBoundedMemory.Span, valueBoundedMemory.Span, options)); - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { Assert.Equal(expected, compareInfo.IsSuffix(sourceBoundedMemory.Span, valueBoundedMemory.Span, options, out int actualMatchLength)); Assert.Equal(expectedMatchLength, actualMatchLength); @@ -162,7 +162,7 @@ public void IsSuffix(CompareInfo compareInfo, string source, string value, Compa [Fact] public void IsSuffix_UnassignedUnicode() { - bool result = PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnOSX ? false : true; + bool result = PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnApplePlatform ? false : true; int expectedMatchLength = (result) ? 6 : 0; IsSuffix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result, expectedMatchLength); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs index 498290ccf8213e..abb2c31f1679f7 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs @@ -49,7 +49,7 @@ public static IEnumerable LastIndexOf_TestData() // Slovak yield return new object[] { s_slovakCompare, "ch", "h", 0, 1, CompareOptions.None, -1, 0 }; // Android has its own ICU, which doesn't work well with slovak - if (!PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic && !PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic && !PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_slovakCompare, "hore chodit", "HO", 11, 12, CompareOptions.IgnoreCase, 0, 2 }; } @@ -104,7 +104,7 @@ public static IEnumerable LastIndexOf_TestData() yield return new object[] { s_invariantCompare, "AA\u200DA", "\u200d", 3, 4, CompareOptions.None, 4, 0}; // Ignore symbols - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { s_invariantCompare, "More Test's", "Tests", 10, 11, CompareOptions.IgnoreSymbols, 5, 6 }; yield return new object[] { s_invariantCompare, "More Test's", "Tests", 10, 11, CompareOptions.None, -1, 0 }; yield return new object[] { s_invariantCompare, "cbabababdbaba", "ab", 12, 13, CompareOptions.None, 10, 2 }; @@ -120,7 +120,7 @@ public static IEnumerable LastIndexOf_TestData() } // Inputs where matched length does not equal value string length - if (!PlatformDetection.IsHybridGlobalizationOnBrowser && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (!PlatformDetection.IsHybridGlobalizationOnBrowser && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_germanCompare, "abc Strasse Strasse xyz", "stra\u00DFe", 22, 23, supportedIgnoreCaseIgnoreNonSpaceOptions, 12, 7 }; yield return new object[] { s_germanCompare, "abc stra\u00DFe stra\u00DFe xyz", "Strasse", 20, 21, supportedIgnoreCaseIgnoreNonSpaceOptions, 11, 6 }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs index 72d03ec00e3c82..596b0f3435ad1f 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs @@ -152,10 +152,10 @@ public static IEnumerable SortKey_TestData() yield return new object[] { s_invariantCompare, "\u68EE\u9D0E\u5916", "\u68EE\u9DD7\u5916", ignoreKanaIgnoreWidthIgnoreCase, -1 }; yield return new object[] { s_invariantCompare, "\u68EE\u9DD7\u5916", "\u68EE\u9DD7\u5916", ignoreKanaIgnoreWidthIgnoreCase, 0 }; - yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "\u2019", "'", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u2019", "'", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; yield return new object[] { s_invariantCompare, "", "'", ignoreKanaIgnoreWidthIgnoreCase, -1 }; - yield return new object[] { s_invariantCompare, "\u4E00", "\uFF11", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u4E00", "\uFF11", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; yield return new object[] { s_invariantCompare, "\u2160", "\uFF11", ignoreKanaIgnoreWidthIgnoreCase, 1 }; yield return new object[] { s_invariantCompare, "0", "\uFF10", ignoreKanaIgnoreWidthIgnoreCase, 0 }; @@ -163,7 +163,7 @@ public static IEnumerable SortKey_TestData() yield return new object[] { s_invariantCompare, "9999\uFF1910", "1\uFF10", ignoreKanaIgnoreWidthIgnoreCase, 1 }; yield return new object[] { s_invariantCompare, "9999\uFF191010", "1\uFF10", ignoreKanaIgnoreWidthIgnoreCase, 1 }; - yield return new object[] { s_invariantCompare, "'\u3000'", "' '", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 0 }; + yield return new object[] { s_invariantCompare, "'\u3000'", "' '", ignoreKanaIgnoreWidthIgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 0 }; yield return new object[] { s_invariantCompare, "\uFF1B", ";", ignoreKanaIgnoreWidthIgnoreCase, 0 }; yield return new object[] { s_invariantCompare, "\uFF08", "(", ignoreKanaIgnoreWidthIgnoreCase, 0 }; yield return new object[] { s_invariantCompare, "\u30FC", "\uFF70", ignoreKanaIgnoreWidthIgnoreCase, 0 }; @@ -189,13 +189,13 @@ public static IEnumerable SortKey_TestData() yield return new object[] { s_invariantCompare, "\u304D\u3083", "\u30AD\u3083", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u304D \u3083", "\u30AD\u3083", CompareOptions.None, -1 }; - yield return new object[] { s_invariantCompare, "\u3044", "I", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "a", "A", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "a", "\uFF41", CompareOptions.None,PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "ABCDE", "\uFF21\uFF22\uFF23\uFF24\uFF25", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "ABCDE", "\uFF21\uFF22\uFF23D\uFF25", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u3044", "I", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "a", "A", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "a", "\uFF41", CompareOptions.None,PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "ABCDE", "\uFF21\uFF22\uFF23\uFF24\uFF25", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "ABCDE", "\uFF21\uFF22\uFF23D\uFF25", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; yield return new object[] { s_invariantCompare, new string('a', 5555), new string('a', 5554) + "b", CompareOptions.None, -1 }; - yield return new object[] { s_invariantCompare, "ABCDE", "\uFF41\uFF42\uFF23D\uFF25", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "ABCDE", "\uFF41\uFF42\uFF23D\uFF25", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; yield return new object[] { s_invariantCompare, "\u6FA4", "\u6CA2", CompareOptions.None, 1 }; yield return new object[] { s_invariantCompare, "\u3070\u3073\u3076\u3079\u307C", "\u30D0\u30D3\u30D6\u30D9\u30DC", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; @@ -219,65 +219,65 @@ public static IEnumerable SortKey_TestData() yield return new object[] { s_invariantCompare, "\u68EE\u9D0E\u5916", "\u68EE\u9DD7\u5916", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u68EE\u9DD7\u5916", "\u68EE\u9DD7\u5916", CompareOptions.None, 0 }; - yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "\u2019", "'", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u2019\u2019\u2019\u2019", "''''", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u2019", "'", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; yield return new object[] { s_invariantCompare, "", "'", CompareOptions.None, -1 }; - yield return new object[] { s_invariantCompare, "\u4E00", "\uFF11", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u4E00", "\uFF11", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; yield return new object[] { s_invariantCompare, "\u2160", "\uFF11", CompareOptions.None, 1 }; - yield return new object[] { s_invariantCompare, "0", "\uFF10", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "10", "1\uFF10", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "0", "\uFF10", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "10", "1\uFF10", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; yield return new object[] { s_invariantCompare, "1\uFF10", "1\uFF10", CompareOptions.None, 0 }; yield return new object[] { s_invariantCompare, "9999\uFF1910", "1\uFF10", CompareOptions.None, 1 }; yield return new object[] { s_invariantCompare, "9999\uFF191010", "1\uFF10", CompareOptions.None, 1 }; - yield return new object[] { s_invariantCompare, "'\u3000'", "' '", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "\uFF1B", ";", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; - yield return new object[] { s_invariantCompare, "\uFF08", "(", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "'\u3000'", "' '", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\uFF1B", ";", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\uFF08", "(", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : 1 }; yield return new object[] { s_invariantCompare, "\u30FC", "\uFF0D", CompareOptions.None, 1 }; yield return new object[] { s_invariantCompare, "\u30FC", "\u30FC", CompareOptions.None, 0 }; yield return new object[] { s_invariantCompare, "\u30FC", "\u2015", CompareOptions.None, 1 }; yield return new object[] { s_invariantCompare, "\u30FC", "\u2010", CompareOptions.None, 1 }; - yield return new object[] { s_invariantCompare, "/", "\uFF0F", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "'", "\uFF07", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\"", "\uFF02", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "/", "\uFF0F", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "'", "\uFF07", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\"", "\uFF02", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; // Turkish yield return new object[] { s_turkishCompare, "i", "I", CompareOptions.None, 1 }; // Android has its own ICU, which doesn't work well with tr - if (!PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (!PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_turkishCompare, "i", "I", CompareOptions.IgnoreCase, 1 }; yield return new object[] { s_turkishCompare, "i", "\u0130", CompareOptions.IgnoreCase, 0 }; } - yield return new object[] { s_invariantCompare, "i", "\u0130", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "i", "I", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "i", "\u0130", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "i", "I", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; yield return new object[] { s_invariantCompare, "i", "I", CompareOptions.IgnoreCase, 0 }; - yield return new object[] { s_invariantCompare, "i", "\u0130", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "i", "\u0130", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; yield return new object[] { s_invariantCompare, "i", "\u0130", CompareOptions.IgnoreCase, -1 }; yield return new object[] { s_invariantCompare, "\u00C0", "A\u0300", CompareOptions.None, 0 }; - yield return new object[] { s_invariantCompare, "\u00C0", "a\u0300", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? - 1 : 1 }; + yield return new object[] { s_invariantCompare, "\u00C0", "a\u0300", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? - 1 : 1 }; yield return new object[] { s_invariantCompare, "\u00C0", "a\u0300", CompareOptions.IgnoreCase, 0 }; yield return new object[] { s_invariantCompare, "FooBA\u0300R", "FooB\u00C0R", CompareOptions.IgnoreNonSpace, 0 }; yield return new object[] { s_invariantCompare, new string('a', 5555), new string('a', 5555), CompareOptions.None, 0 }; yield return new object[] { s_invariantCompare, "foobar", "FooB\u00C0R", CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase, 0 }; - yield return new object[] { s_invariantCompare, "foobar", "FooB\u00C0R", CompareOptions.IgnoreNonSpace, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "foobar", "FooB\u00C0R", CompareOptions.IgnoreNonSpace, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.IgnoreWidth, 0 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.IgnoreCase, -1 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.IgnoreWidth, 0 }; - yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreWidth, 0 }; - yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreCase, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : s_expectedHalfToFullFormsComparison }; - yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreNonSpace, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : s_expectedHalfToFullFormsComparison }; - yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnOSX ? -1 : s_expectedHalfToFullFormsComparison }; + yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreCase, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : s_expectedHalfToFullFormsComparison }; + yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreNonSpace, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : s_expectedHalfToFullFormsComparison }; + yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.None, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? -1 : s_expectedHalfToFullFormsComparison }; yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.IgnoreCase, s_expectedHiraganaToKatakanaCompare }; @@ -285,9 +285,9 @@ public static IEnumerable SortKey_TestData() yield return new object[] { new CultureInfo("es-ES").CompareInfo, "llegar", "lugar", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u3060", "\u30C0", CompareOptions.IgnoreKanaType, 0 }; - yield return new object[] { s_invariantCompare, "c", "C", CompareOptions.IgnoreKanaType, PlatformDetection.IsHybridGlobalizationOnOSX ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "c", "C", CompareOptions.IgnoreKanaType, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? 1 : -1 }; - if (PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "\uFF9E", "\u3099", CompareOptions.IgnoreNonSpace, 0 }; yield return new object[] { s_invariantCompare, "\uFF9E", "\u3099", CompareOptions.IgnoreCase, 0 }; @@ -408,7 +408,7 @@ public void SortKeyTest(CompareInfo compareInfo, string string1, string string2, Assert.Equal(expectedSign, Math.Sign(SortKey.Compare(sk1, sk2))); Assert.Equal(expectedSign == 0, sk1.Equals(sk2)); - if (!WindowsVersionHasTheCompareStringRegression && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (!WindowsVersionHasTheCompareStringRegression && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { Assert.Equal(Math.Sign(compareInfo.Compare(string1, string2, options)), Math.Sign(SortKey.Compare(sk1, sk2))); } diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoAll.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoAll.cs index c707ff005faf3f..4e3996bff80eb1 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoAll.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoAll.cs @@ -652,7 +652,7 @@ public static IEnumerable CultureInfo_TestData() yield return new object[] { 0x40404, new [] { "zh-tw_radstr", "zh-tw" }, "zh-TW", "zho", "CHT", "zh-Hant-TW", "zh-TW" }; yield return new object[] { 0x40411, new [] { "ja-jp_radstr", "ja-jp" }, "ja-JP", "jpn", "JPN", "ja-JP", "ja-JP" }; yield return new object[] { 0x40c04, new [] { "zh-hk_radstr", "zh-hk" }, "zh-HK", "zho", "ZHH", "zh-Hant-HK", "zh-HK" }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { 0x281a, new [] { "sr-cyrl-rs" }, "sr-Cyrl-RS", "srp", "SRO", "sr-Cyrl-RS", "sr-Cyrl-RS" }; yield return new object[] { 0x6c1a, new [] { "sr-cyrl" }, "sr-Cyrl-RS", "srp", "SRO", "sr-Cyrl", "sr-Cyrl-RS" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoCtor.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoCtor.cs index 9edf9c5dfe6cfc..c921165a4f3c9e 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoCtor.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoCtor.cs @@ -357,7 +357,7 @@ public static IEnumerable Ctor_String_TestData() yield return new object[] { "zu-ZA", new [] { "zu-ZA" }}; yield return new object[] { CultureInfo.CurrentCulture.Name, new [] { CultureInfo.CurrentCulture.Name } }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "az-Latn", new [] { "az-Latn" }}; yield return new object[] { "az-Latn-AZ", new [] { "az-Latn-AZ" }}; @@ -400,7 +400,7 @@ public void Ctor_String(string name, string[] expectedNames) Assert.Equal(cultureName, culture.ToString(), ignoreCase: true); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform))] public void Ctor_String_Invalid() { AssertExtensions.Throws("name", () => new CultureInfo(null)); // Name is null @@ -446,7 +446,7 @@ public void TestCreationWithTemporaryLCID(int lcid) [InlineData("de-DE-u-co-phonebk-t-xx", "de-DE-t-xx", "de-DE-t-xx_phoneboo")] [InlineData("de-DE-u-co-phonebk-t-xx-u-yy", "de-DE-t-xx-u-yy", "de-DE-t-xx-u-yy_phoneboo")] [InlineData("de-DE", "de-DE", "de-DE")] - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybrid))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] public void TestCreationWithMangledSortName(string cultureName, string expectedCultureName, string expectedSortName) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); @@ -461,7 +461,7 @@ public void TestCreationWithMangledSortName(string cultureName, string expectedC [InlineData("qps-plocm", "qps-PLOCM")] // ICU normalize this name to "qps--plocm" which we normalize it back to "qps-plocm" [InlineData("zh_CN", "zh_cn")] [InlineData("km_KH", "km_kh")] - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybrid), nameof(PlatformDetection.IsNotWindowsServerCore))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser), nameof(PlatformDetection.IsNotWindowsServerCore))] public void TestCreationWithICUNormalizedNames(string cultureName, string expectedCultureName) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoEnglishName.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoEnglishName.cs index a1b5cf9445caa7..02ee539981adbc 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoEnglishName.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoEnglishName.cs @@ -15,7 +15,7 @@ public static IEnumerable EnglishName_TestData() { yield return new object[] { CultureInfo.CurrentCulture.Name, CultureInfo.CurrentCulture.EnglishName }; - if (SupportFullGlobalizationData || PlatformDetection.IsHybridGlobalizationOnOSX) + if (SupportFullGlobalizationData || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { yield return new object[] { "en-US", "English (United States)" }; yield return new object[] { "fr-FR", "French (France)" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNames.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNames.cs index 2cce0e5b842c62..d2c0828bc68503 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNames.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNames.cs @@ -10,7 +10,7 @@ namespace System.Globalization.Tests { public class CultureInfoNames { - private static bool SupportFullIcuResources => (PlatformDetection.IsNotMobile && PlatformDetection.IsIcuGlobalization) || PlatformDetection.IsHybridGlobalizationOnOSX; + private static bool SupportFullIcuResources => (PlatformDetection.IsNotMobile && PlatformDetection.IsIcuGlobalization) || PlatformDetection.IsHybridGlobalizationOnApplePlatform; [ConditionalTheory(nameof(SupportFullIcuResources))] [InlineData("en", "en", "English", "English")] diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNativeName.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNativeName.cs index 35ecb0039df563..9a834dbf2d1dbd 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNativeName.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/CultureInfoNativeName.cs @@ -13,7 +13,7 @@ public static IEnumerable NativeName_TestData() yield return new object[] { CultureInfo.CurrentCulture.Name, CultureInfo.CurrentCulture.NativeName }; // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { yield return new object[] { "en-US", "English (United States)" }; yield return new object[] { "en-CA", "English (Canada)" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/GetCultureInfo.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/GetCultureInfo.cs index 75191ba8b44517..ee72d3cdfc8bbb 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/GetCultureInfo.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CultureInfo/GetCultureInfo.cs @@ -21,7 +21,7 @@ public static IEnumerable GetCultureInfoTestData() yield return new object[] { "ja-JP" }; yield return new object[] { "ar-SA" }; yield return new object[] { "xx-XX" }; - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "de-AT-1901" }; } @@ -33,19 +33,19 @@ public static IEnumerable GetCultureInfoTestData() yield return new object[] { "zh-Hant-CN" }; yield return new object[] { "zh-Hant-SG" }; - if (PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsIcuGlobalization || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { if (PlatformDetection.IsNotWindows) { yield return new object[] { "x\u0000X-Yy", "x" }; // Null byte - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "zh-cmn", "zh-CMN" }; yield return new object[] { "zh-CMN-HANS" }; yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" }; } } - if (!PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "sgn-BE-FR" }; } diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs index 0c23b33cdc1fb9..d0b2af309ea04f 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetAbbreviatedEraName.cs @@ -208,7 +208,7 @@ public static IEnumerable GetAbbreviatedEraName_TestData() } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform))] [MemberData(nameof(GetAbbreviatedEraName_TestData))] public void GetAbbreviatedEraName_Invoke_ReturnsExpected(string cultureName, int era, string expected) { diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs index 6182546199a920..6ef4c405f82728 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoGetEra.cs @@ -49,7 +49,7 @@ public static IEnumerable GetEra_TestData() yield return new object[] { frFRFormat, "ap J-C", -1 }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform))] [MemberData(nameof(GetEra_TestData))] public void GetEra_Invoke_ReturnsExpected(DateTimeFormatInfo format, string eraName, int expected) { diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs index 6e09c2f9e5eaa3..6e00c34d4ca926 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs @@ -67,7 +67,7 @@ public void NativeCalendarName_Get_ReturnsExpected(DateTimeFormatInfo dtfi, Cale { dtfi.Calendar = calendar; - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { // Mobile / Browser ICU doesn't contain NativeCalendarName, Assert.Equal(nativeCalendarName, dtfi.NativeCalendarName); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuAppLocal/IcuAppLocal.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuAppLocal/IcuAppLocal.cs index 2e53286298a844..e26c98bed7ff44 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuAppLocal/IcuAppLocal.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuAppLocal/IcuAppLocal.cs @@ -10,7 +10,7 @@ namespace System.Globalization.Tests { public class IcuAppLocalTests { - private static bool SupportsIcuPackageDownload => PlatformDetection.IsNotHybridGlobalizationOnOSX && RemoteExecutor.IsSupported && + private static bool SupportsIcuPackageDownload => PlatformDetection.IsNotHybridGlobalizationOnApplePlatform && RemoteExecutor.IsSupported && ((PlatformDetection.IsWindows && !PlatformDetection.IsArmProcess) || (PlatformDetection.IsLinux && (PlatformDetection.IsX64Process || PlatformDetection.IsArm64Process) && !PlatformDetection.IsAlpine && !PlatformDetection.IsLinuxBionic)); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuTests.cs index 05b0e950d74321..fe5feb17dd5995 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/IcuTests.cs @@ -9,7 +9,7 @@ namespace System.Globalization.Tests { public class IcuTests { - private static bool IsIcuCompatiblePlatform => !PlatformDetection.IsHybridGlobalizationOnOSX && PlatformDetection.IsNotWindows || + private static bool IsIcuCompatiblePlatform => PlatformDetection.IsNotHybridGlobalizationOnApplePlatform && PlatformDetection.IsNotWindows || ((PlatformDetection.IsWindowsServer2019 || PlatformDetection.IsWindows10Version1903OrGreater) && // Server core doesn't have icu.dll on SysWOW64 !(PlatformDetection.IsWindowsServerCore && PlatformDetection.IsX86Process)); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/NumberFormatInfo/NumberFormatInfoData.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/NumberFormatInfo/NumberFormatInfoData.cs index 96911b398714bf..4ed15eacfec73d 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/NumberFormatInfo/NumberFormatInfoData.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/NumberFormatInfo/NumberFormatInfoData.cs @@ -7,7 +7,7 @@ internal static class NumberFormatInfoData { public static int[] UrINNumberGroupSizes() { - if (PlatformDetection.WindowsVersion >= 10 || PlatformDetection.ICUVersion.Major >= 55 || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.WindowsVersion >= 10 || PlatformDetection.ICUVersion.Major >= 55 || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { return new int[] { 3 }; } diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/RegionInfoTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/RegionInfoTests.cs index a5e4641290aade..e05fd4e1a7422c 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/RegionInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/RegionInfoTests.cs @@ -112,7 +112,7 @@ public void DisplayName(string name, string expected) public static IEnumerable NativeName_TestData() { // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { yield return new object[] { "GB", "United Kingdom" }; yield return new object[] { "SE", "Sverige" }; @@ -137,7 +137,7 @@ public void NativeName(string name, string expected) public static IEnumerable EnglishName_TestData() { // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotUsingLimitedCultures || PlatformDetection.IsAndroid || PlatformDetection.IsHybridGlobalizationOnApplePlatform) { yield return new object[] { "en-US", new string[] { "United States" } }; yield return new object[] { "US", new string[] { "United States" } }; @@ -208,7 +208,7 @@ public static IEnumerable RegionInfo_TestData() "SAU", "SAU" }; yield return new object[] { 0x412, 134, "South Korean Won", "KRW", "Korean Won", PlatformDetection.IsNlsGlobalization ? "\uc6d0" : "\ub300\ud55c\ubbfc\uad6d\u0020\uc6d0", "KOR", "KOR" }; yield return new object[] { 0x40d, 117, "Israeli New Shekel", "ILS", "Israeli New Sheqel", - PlatformDetection.IsNlsGlobalization || PlatformDetection.ICUVersion.Major >= 58 || PlatformDetection.IsHybridGlobalizationOnOSX ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" }; + PlatformDetection.IsNlsGlobalization || PlatformDetection.ICUVersion.Major >= 58 || PlatformDetection.IsHybridGlobalizationOnApplePlatform ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" }; } [Theory] @@ -219,7 +219,7 @@ public void MiscTest(int lcid, int geoId, string currencyEnglishName, string cur Assert.Equal(geoId, ri.GeoId); // Android has its own ICU, which doesn't 100% map to UsingLimitedCultures - if (PlatformDetection.IsUsingLimitedCultures && !PlatformDetection.IsAndroid && !PlatformDetection.IsHybridGlobalizationOnOSX) + if (PlatformDetection.IsUsingLimitedCultures && !PlatformDetection.IsAndroid && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { Assert.Equal(currencyShortName, ri.CurrencyEnglishName); Assert.Equal(currencyShortName, ri.CurrencyNativeName); 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 3079f6f0129f6a..edffac427928fd 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs @@ -222,7 +222,7 @@ public void CultureName_Set(AssemblyName assemblyName, string originalCultureNam } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95195", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void CultureName_Set_Invalid_ThrowsCultureNotFoundException() { var assemblyName = new AssemblyName("Test"); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Convert.ToDateTime.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Convert.ToDateTime.cs index 245ccd8f3af4fd..f611278d19c800 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Convert.ToDateTime.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Convert.ToDateTime.cs @@ -11,7 +11,7 @@ public class ConvertToDateTimeTests : ConvertTestBase private static readonly DateTimeFormatInfo s_dateTimeFormatInfo = new DateTimeFormatInfo(); [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void FromString() { DateTime[] expectedValues = { new DateTime(1999, 12, 31, 23, 59, 59), new DateTime(100, 1, 1, 0, 0, 0), new DateTime(2216, 2, 29, 0, 0, 0), new DateTime(1, 1, 1, 0, 0, 0) }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs index ec855aca2d8842..2b8c983fc83cd3 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs @@ -179,7 +179,7 @@ public static IEnumerable CreateFromCultureAndOptionsData() yield return new object[] { "turky i", "TURKY \u0130", "tr-TR", CompareOptions.IgnoreCase, true}; } - if (PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { bool ignoreSymbolsIgnoresOnlyPunctuation = PlatformDetection.IsHybridGlobalizationOnBrowser; yield return new object[] { "abcd", "ab cd", "en-US", CompareOptions.IgnoreSymbols, true }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateTimeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateTimeTests.cs index e5cec1467d318d..6fb4bc671bf4f1 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateTimeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateTimeTests.cs @@ -2003,7 +2003,7 @@ public static IEnumerable Parse_ValidInput_Succeeds_MemberData() } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [MemberData(nameof(Parse_ValidInput_Succeeds_MemberData))] public static void Parse_ValidInput_Succeeds(string input, CultureInfo culture, DateTime? expected) { @@ -2464,7 +2464,7 @@ public static IEnumerable ToString_MatchesExpected_MemberData() } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [MemberData(nameof(Parse_ValidInput_Succeeds_MemberData))] public static void Parse_Span_ValidInput_Succeeds(string input, CultureInfo culture, DateTime? expected) { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs index 765558c60ad6a8..b31f71b1e3b48c 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs @@ -89,7 +89,7 @@ public static IEnumerable GetHashCodeOrdinalIgnoreCase_TestData() { yield return new object[] { "AaBbCcDdEeFfGgHh".Insert(i, "\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */) }; yield return new object[] { "AaBbCcDdEeFfGgHh".Insert(i, "\u044D" /* CYRILLIC SMALL LETTER E */) }; - if (PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "AaBbCcDdEeFfGgHh".Insert(i, "\u0131" /* LATIN SMALL LETTER DOTLESS I */) }; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs index 7cc19ecc0ec6a6..d732e1644bb296 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs @@ -232,7 +232,7 @@ public static IEnumerable Contains_String_StringComparison_TestData() yield return new object[] { "Hello", "", StringComparison.CurrentCulture, true }; yield return new object[] { "Hello", "Ell" + SoftHyphen, StringComparison.CurrentCulture, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", "ell" + SoftHyphen, StringComparison.CurrentCulture, true }; // CurrentCultureIgnoreCase @@ -245,7 +245,7 @@ public static IEnumerable Contains_String_StringComparison_TestData() yield return new object[] { "", "hello", StringComparison.CurrentCultureIgnoreCase, false }; yield return new object[] { "Hello", "", StringComparison.CurrentCultureIgnoreCase, true }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "Hello", "ell" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true }; yield return new object[] { "Hello", "Ell" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true }; @@ -262,7 +262,7 @@ public static IEnumerable Contains_String_StringComparison_TestData() yield return new object[] { "Hello", "", StringComparison.InvariantCulture, true }; yield return new object[] { "Hello", "Ell" + SoftHyphen, StringComparison.InvariantCulture, false }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "Hello", "ell" + SoftHyphen, StringComparison.InvariantCulture, true }; // InvariantCultureIgnoreCase @@ -275,7 +275,7 @@ public static IEnumerable Contains_String_StringComparison_TestData() yield return new object[] { "", "hello", StringComparison.InvariantCultureIgnoreCase, false }; yield return new object[] { "Hello", "", StringComparison.InvariantCultureIgnoreCase, true }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "Hello", "ell" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true }; yield return new object[] { "Hello", "Ell" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true }; @@ -700,7 +700,7 @@ public static IEnumerable Replace_StringComparison_TestData() yield return new object[] { "abc", "b", "d", StringComparison.CurrentCulture, "adc" }; yield return new object[] { "abc", "b", null, StringComparison.CurrentCulture, "ac" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.CurrentCulture, "def" }; yield return new object[] { "abc", "abc", "def", StringComparison.CurrentCultureIgnoreCase, "def" }; @@ -710,7 +710,7 @@ public static IEnumerable Replace_StringComparison_TestData() yield return new object[] { "abc", "b", "d", StringComparison.CurrentCultureIgnoreCase, "adc" }; yield return new object[] { "abc", "b", null, StringComparison.CurrentCultureIgnoreCase, "ac" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.CurrentCultureIgnoreCase, "def" }; yield return new object[] { "abc", "abc", "def", StringComparison.Ordinal, "def" }; @@ -720,7 +720,7 @@ public static IEnumerable Replace_StringComparison_TestData() yield return new object[] { "abc", "b", "d", StringComparison.Ordinal, "adc" }; yield return new object[] { "abc", "b", null, StringComparison.Ordinal, "ac" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.Ordinal, "abc" }; yield return new object[] { "abc", "abc", "def", StringComparison.OrdinalIgnoreCase, "def" }; @@ -731,7 +731,7 @@ public static IEnumerable Replace_StringComparison_TestData() yield return new object[] { "abc", "b", null, StringComparison.OrdinalIgnoreCase, "ac" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.OrdinalIgnoreCase, "abc" }; yield return new object[] { "abc", "abc", "def", StringComparison.InvariantCulture, "def" }; @@ -742,7 +742,7 @@ public static IEnumerable Replace_StringComparison_TestData() yield return new object[] { "abc", "b", null, StringComparison.InvariantCulture, "ac" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.InvariantCulture, "def" }; yield return new object[] { "abc", "abc", "def", StringComparison.InvariantCultureIgnoreCase, "def" }; @@ -753,7 +753,7 @@ public static IEnumerable Replace_StringComparison_TestData() yield return new object[] { "abc", "b", null, StringComparison.InvariantCultureIgnoreCase, "ac" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "abc", "abc" + SoftHyphen, "def", StringComparison.InvariantCultureIgnoreCase, "def" }; @@ -825,7 +825,7 @@ public static IEnumerable Replace_StringComparisonCulture_TestData() yield return new object[] { "abc", "abc", "def", true, CultureInfo.InvariantCulture, "def" }; yield return new object[] { "abc", "ABC", "def", true, CultureInfo.InvariantCulture, "def" }; - if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX) + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { "abc", "abc" + SoftHyphen, "def", false, null, "def" }; yield return new object[] { "abc", "abc" + SoftHyphen, "def", true, null, "def" }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs index 82611495e694b1..bb500cea493b6d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs @@ -70,7 +70,7 @@ public static void Casing_Invariant(int original, int upper, int lower) [InlineData('\u0131', '\u0131', '\u0131')] // U+0131 LATIN SMALL LETTER DOTLESS I [InlineData(0x10400, 0x10400, 0x10428)] // U+10400 DESERET CAPITAL LETTER LONG I [InlineData(0x10428, 0x10400, 0x10428)] // U+10428 DESERET SMALL LETTER LONG I - [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public static void ICU_Casing_Invariant(int original, int upper, int lower) { var rune = new Rune(original); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeZoneInfoTests.cs index 11d6c3d20a6fbe..a927e0a129903b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeZoneInfoTests.cs @@ -89,7 +89,7 @@ public static void Names() // Name abbreviations, if available, are used instead public static IEnumerable Platform_TimeZoneNamesTestData() { - if (PlatformDetection.IsBrowser || (!PlatformDetection.IsHybridGlobalizationOnOSX && (PlatformDetection.IsMacCatalyst || PlatformDetection.IsiOS || PlatformDetection.IstvOS))) + if (PlatformDetection.IsBrowser || (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform && (PlatformDetection.IsMacCatalyst || PlatformDetection.IsiOS || PlatformDetection.IstvOS))) return new TheoryData { { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) America/Los_Angeles", null, "PST", "PDT", null }, @@ -100,7 +100,7 @@ public static IEnumerable Platform_TimeZoneNamesTestData() { s_NewfoundlandTz, "(UTC-03:30) America/St_Johns", null, "NST", "NDT", null }, { s_catamarcaTz, "(UTC-03:00) America/Argentina/Catamarca", null, "-03", "-02", null } }; - else if (PlatformDetection.IsHybridGlobalizationOnOSX && (PlatformDetection.IsMacCatalyst || PlatformDetection.IsiOS || PlatformDetection.IstvOS)) + else if (PlatformDetection.IsHybridGlobalizationOnApplePlatform && (PlatformDetection.IsMacCatalyst || PlatformDetection.IsiOS || PlatformDetection.IstvOS)) return new TheoryData { { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) America/Los_Angeles", null, "Pacific Standard Time", "Pacific Daylight Time", "Pacific Summer Time" }, diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 623944f3ec464e..933eb8aeb06bb3 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -755,6 +755,7 @@ elseif(HOST_IOS OR HOST_TVOS OR HOST_MACCAT) elseif(HOST_MACCAT) string(APPEND ICU_FLAGS " -DTARGET_MACCATALYST") endif() + string(APPEND ICU_FLAGS " -DAPPLE_HYBRID_GLOBALIZATION") set(HAVE_SYS_ICU 1) set(STATIC_ICU 1) elseif(HOST_ANDROID) diff --git a/src/native/libs/System.Globalization.Native/CMakeLists.txt b/src/native/libs/System.Globalization.Native/CMakeLists.txt index 11e8401a0c14ea..1959fec40a7cfd 100644 --- a/src/native/libs/System.Globalization.Native/CMakeLists.txt +++ b/src/native/libs/System.Globalization.Native/CMakeLists.txt @@ -75,6 +75,7 @@ if (NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CM endif() if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) + add_definitions(-DAPPLE_HYBRID_GLOBALIZATION) set(NATIVEGLOBALIZATION_SOURCES ${NATIVEGLOBALIZATION_SOURCES} pal_placeholders.c diff --git a/src/native/libs/System.Globalization.Native/entrypoints.c b/src/native/libs/System.Globalization.Native/entrypoints.c index 0883c5c08dc0ea..f2db315ec5452e 100644 --- a/src/native/libs/System.Globalization.Native/entrypoints.c +++ b/src/native/libs/System.Globalization.Native/entrypoints.c @@ -55,7 +55,7 @@ static const Entry s_globalizationNative[] = DllImportEntry(GlobalizationNative_NormalizeString) DllImportEntry(GlobalizationNative_StartsWith) DllImportEntry(GlobalizationNative_WindowsIdToIanaId) -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) DllImportEntry(GlobalizationNative_ChangeCaseInvariantNative) DllImportEntry(GlobalizationNative_ChangeCaseNative) DllImportEntry(GlobalizationNative_CompareStringNative) diff --git a/src/native/libs/System.Globalization.Native/pal_calendarData.h b/src/native/libs/System.Globalization.Native/pal_calendarData.h index cc0116b7e86ec9..31ce76d8693952 100644 --- a/src/native/libs/System.Globalization.Native/pal_calendarData.h +++ b/src/native/libs/System.Globalization.Native/pal_calendarData.h @@ -101,7 +101,7 @@ PALEXPORT int32_t GlobalizationNative_GetJapaneseEraStartDate(int32_t era, int32_t* startYear, int32_t* startMonth, int32_t* startDay); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT const char* GlobalizationNative_GetCalendarInfoNative(const char* localeName, CalendarId calendarId, CalendarDataType dataType); diff --git a/src/native/libs/System.Globalization.Native/pal_calendarData.m b/src/native/libs/System.Globalization.Native/pal_calendarData.m index 8cfa15000fc694..9520e176467542 100644 --- a/src/native/libs/System.Globalization.Native/pal_calendarData.m +++ b/src/native/libs/System.Globalization.Native/pal_calendarData.m @@ -10,7 +10,7 @@ #error This file relies on ARC for memory management, but ARC is not enabled. #endif -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS +#if defined(APPLE_HYBRID_GLOBALIZATION) /* Function: GetCalendarIdentifier diff --git a/src/native/libs/System.Globalization.Native/pal_casing.h b/src/native/libs/System.Globalization.Native/pal_casing.h index 088153ffa489b4..5371ba3bb08868 100644 --- a/src/native/libs/System.Globalization.Native/pal_casing.h +++ b/src/native/libs/System.Globalization.Native/pal_casing.h @@ -23,7 +23,7 @@ PALEXPORT void GlobalizationNative_ChangeCaseTurkish(const UChar* lpSrc, UChar* lpDst, int32_t cwDstLength, int32_t bToUpper); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT int32_t GlobalizationNative_ChangeCaseNative(const uint16_t* localeName, int32_t lNameLength, const uint16_t* lpSrc, diff --git a/src/native/libs/System.Globalization.Native/pal_casing.m b/src/native/libs/System.Globalization.Native/pal_casing.m index 8f826769a603e4..ba5c853c0b4c61 100644 --- a/src/native/libs/System.Globalization.Native/pal_casing.m +++ b/src/native/libs/System.Globalization.Native/pal_casing.m @@ -11,7 +11,7 @@ #error This file relies on ARC for memory management, but ARC is not enabled. #endif -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS +#if defined(APPLE_HYBRID_GLOBALIZATION) /** * Is this code unit a lead surrogate (U+d800..U+dbff)? * @param c 16-bit code unit diff --git a/src/native/libs/System.Globalization.Native/pal_collation.h b/src/native/libs/System.Globalization.Native/pal_collation.h index c9bde6cab0f198..b2ef6e1ed3d247 100644 --- a/src/native/libs/System.Globalization.Native/pal_collation.h +++ b/src/native/libs/System.Globalization.Native/pal_collation.h @@ -65,7 +65,7 @@ PALEXPORT int32_t GlobalizationNative_GetSortKey(SortHandle* pSortHandle, uint8_t* sortKey, int32_t cbSortKeyLength, int32_t options); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT int32_t GlobalizationNative_CompareStringNative(const uint16_t* localeName, int32_t lNameLength, const uint16_t* lpTarget, diff --git a/src/native/libs/System.Globalization.Native/pal_collation.m b/src/native/libs/System.Globalization.Native/pal_collation.m index aa51b8d6ba1387..9a63c11be44abc 100644 --- a/src/native/libs/System.Globalization.Native/pal_collation.m +++ b/src/native/libs/System.Globalization.Native/pal_collation.m @@ -11,7 +11,7 @@ #error This file relies on ARC for memory management, but ARC is not enabled. #endif -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS +#if defined(APPLE_HYBRID_GLOBALIZATION) // Enum that corresponds to C# CompareOptions typedef enum { diff --git a/src/native/libs/System.Globalization.Native/pal_icushim_internal.h b/src/native/libs/System.Globalization.Native/pal_icushim_internal.h index 88026f9ddec489..a7bad69cef3197 100644 --- a/src/native/libs/System.Globalization.Native/pal_icushim_internal.h +++ b/src/native/libs/System.Globalization.Native/pal_icushim_internal.h @@ -21,7 +21,7 @@ // All ICU headers need to be included here so that all function prototypes are // available before the function pointers are declared below. -#if defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS) +#if defined(APPLE_HYBRID_GLOBALIZATION) #include #include #include @@ -352,7 +352,7 @@ const char* GlobalizationNative_GetICUDataPathFallback(void); #endif #endif // !defined(STATIC_ICU) -#if defined(TARGET_MACCATALYST) || defined(TARGET_IOS) || defined(TARGET_TVOS) +#if defined(APPLE_HYBRID_GLOBALIZATION) /** * Append a code point to a string, overwriting 1 or 2 code units. * The offset points to the current end of the string contents diff --git a/src/native/libs/System.Globalization.Native/pal_icushim_static.c b/src/native/libs/System.Globalization.Native/pal_icushim_static.c index 052fe9285726b6..ecef84ab793350 100644 --- a/src/native/libs/System.Globalization.Native/pal_icushim_static.c +++ b/src/native/libs/System.Globalization.Native/pal_icushim_static.c @@ -11,7 +11,7 @@ #include #include -#if !defined(TARGET_MACCATALYST) && !defined(TARGET_IOS) && !defined(TARGET_TVOS) +#if !defined(APPLE_HYBRID_GLOBALIZATION) #include #endif diff --git a/src/native/libs/System.Globalization.Native/pal_locale.h b/src/native/libs/System.Globalization.Native/pal_locale.h index e52cc62fde71ad..8ded749d3c11c9 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.h +++ b/src/native/libs/System.Globalization.Native/pal_locale.h @@ -16,7 +16,7 @@ PALEXPORT int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName, int shortFormat, UChar* value, int32_t valueLength); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT const char* GlobalizationNative_GetDefaultLocaleNameNative(void); diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 4d35dbe0dcddbf..873b56950ee3c3 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -36,7 +36,7 @@ return strdup([localeName UTF8String]); } -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS +#if defined(APPLE_HYBRID_GLOBALIZATION) const char* GlobalizationNative_GetLocaleNameNative(const char* localeName) { @autoreleasepool diff --git a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h index 495d1114ec6ca9..f609c654e12925 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h @@ -42,7 +42,7 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoGroupingSizes(const UChar* lo LocaleNumberData localeGroupingData, int32_t* primaryGroupSize, int32_t* secondaryGroupSize); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeNumberData); diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.h b/src/native/libs/System.Globalization.Native/pal_localeStringData.h index 5698a6a49c1b0d..957021dffa697a 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeStringData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.h @@ -48,7 +48,7 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeNam UChar* value, int32_t valueLength, const UChar* uiLocaleName); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData, const char* currentUILocaleName); #endif diff --git a/src/native/libs/System.Globalization.Native/pal_normalization.h b/src/native/libs/System.Globalization.Native/pal_normalization.h index 50d9ef7d706c51..a05ac2005543e7 100644 --- a/src/native/libs/System.Globalization.Native/pal_normalization.h +++ b/src/native/libs/System.Globalization.Native/pal_normalization.h @@ -26,7 +26,7 @@ PALEXPORT int32_t GlobalizationNative_NormalizeString(NormalizationForm normaliz int32_t cwSrcLength, UChar* lpDst, int32_t cwDstLength); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT int32_t GlobalizationNative_IsNormalizedNative(NormalizationForm normalizationForm, const uint16_t* lpStr, int32_t cwStrLength); diff --git a/src/native/libs/System.Globalization.Native/pal_normalization.m b/src/native/libs/System.Globalization.Native/pal_normalization.m index ac3a2fd5cb521b..c029a16328bb24 100644 --- a/src/native/libs/System.Globalization.Native/pal_normalization.m +++ b/src/native/libs/System.Globalization.Native/pal_normalization.m @@ -11,7 +11,7 @@ #error This file relies on ARC for memory management, but ARC is not enabled. #endif -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS +#if defined(APPLE_HYBRID_GLOBALIZATION) static NSString* GetNormalizedStringForForm(NormalizationForm normalizationForm, NSString* sourceString) { switch (normalizationForm) diff --git a/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.h b/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.h index 86d438ef96e8e1..bb32c41d43db9d 100644 --- a/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.h +++ b/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.h @@ -23,7 +23,7 @@ typedef enum PALEXPORT int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, const char* region, UChar* ianaId, int32_t ianaIdLength); PALEXPORT int32_t GlobalizationNative_IanaIdToWindowsId(const UChar* ianaId, UChar* windowsId, int32_t windowsIdLength); PALEXPORT ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName, const UChar* timeZoneId, TimeZoneDisplayNameType type, UChar* result, int32_t resultLength); -#if (defined(__APPLE__) && (TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS)) +#if defined(APPLE_HYBRID_GLOBALIZATION) PALEXPORT int32_t GlobalizationNative_GetTimeZoneDisplayNameNative(const uint16_t* localeName, int32_t lNameLength, const uint16_t* timeZoneId, int32_t timeZoneIdLength, TimeZoneDisplayNameType type, uint16_t* result, int32_t resultLength); #endif diff --git a/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.m b/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.m index 017f982ebb2c90..e5404e5cb0d4c5 100644 --- a/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.m +++ b/src/native/libs/System.Globalization.Native/pal_timeZoneInfo.m @@ -11,7 +11,7 @@ #error This file relies on ARC for memory management, but ARC is not enabled. #endif -#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS +#if defined(APPLE_HYBRID_GLOBALIZATION) /* Gets the localized display name that is currently in effect for the specified time zone. */ From 2e595fb6908bf0266c91b5586514aaab60f1502b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 18 Jan 2024 23:44:25 +0900 Subject: [PATCH 096/189] Small cleanup in the cctor interpreter (#97141) I was looking at the IsValueType handling here and decided to clean it up. --- .../ILCompiler.Compiler/Compiler/TypePreinit.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs index 5564907e4bc248..54ff6c6c544afe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs @@ -1860,9 +1860,7 @@ private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out V return spanRef.TryAccessElement(spanIndex.AsInt32(), out retVal); } return false; - case "GetTypeFromHandle" when method.OwningType is MetadataType typeType - && typeType.Name == "Type" && typeType.Namespace == "System" - && typeType.Module == typeType.Context.SystemModule + case "GetTypeFromHandle" when IsSystemType(method.OwningType) && parameters[0] is RuntimeTypeHandleValue typeHandle: { if (!_internedTypes.TryGetValue(typeHandle.Type, out RuntimeTypeValue runtimeType)) @@ -1872,17 +1870,13 @@ private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out V retVal = runtimeType; return true; } - case "get_IsValueType" when method.OwningType is MetadataType typeType - && typeType.Name == "Type" && typeType.Namespace == "System" - && typeType.Module == typeType.Context.SystemModule + case "get_IsValueType" when IsSystemType(method.OwningType) && parameters[0] is RuntimeTypeValue typeToCheckForValueType: { retVal = ValueTypeValue.FromSByte(typeToCheckForValueType.TypeRepresented.IsValueType ? (sbyte)1 : (sbyte)0); return true; } - case "op_Equality" when method.OwningType is MetadataType typeType - && typeType.Name == "Type" && typeType.Namespace == "System" - && typeType.Module == typeType.Context.SystemModule + case "op_Equality" when IsSystemType(method.OwningType) && (parameters[0] is RuntimeTypeValue || parameters[1] is RuntimeTypeValue): { retVal = ValueTypeValue.FromSByte(parameters[0] == parameters[1] ? (sbyte)1 : (sbyte)0); @@ -1890,6 +1884,11 @@ private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out V } } + static bool IsSystemType(TypeDesc type) + => type is MetadataType typeType + && typeType.Name == "Type" && typeType.Namespace == "System" + && typeType.Module == typeType.Context.SystemModule; + return false; } From fd44d5c66771764d265ba6b607d2db85dbba28b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 19 Jan 2024 00:19:00 +0900 Subject: [PATCH 097/189] Fold GeneratingMetadataManager into MetadataManager (#97140) Long time ago, `MetadataManager` had two descendants: the `GeneratingMetadataManager` and another one that I forgot the name of. It was used for .NET Native and didn't generate metadata, because ILTransforms did. Fold the classes together. --- .../Compiler/AnalysisBasedMetadataManager.cs | 2 +- .../Compiler/GeneratingMetadataManager.cs | 224 ------------------ .../Compiler/MetadataManager.cs | 197 ++++++++++++++- .../Compiler/UsageBasedMetadataManager.cs | 2 +- .../ILCompiler.Compiler.csproj | 1 - 5 files changed, 195 insertions(+), 231 deletions(-) delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index b748cc2c46c02d..8c24c7addbb080 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -19,7 +19,7 @@ namespace ILCompiler /// /// A metadata manager that knows the full set of metadata ahead of time. /// - public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider + public sealed class AnalysisBasedMetadataManager : MetadataManager, ICompilationRootProvider { private readonly List _modulesWithMetadata; private readonly List _typesWithRootedCctorContext; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs deleted file mode 100644 index 1e7442fec74862..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs +++ /dev/null @@ -1,224 +0,0 @@ -// 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.IO; -using System.Collections.Generic; -using System.Text; -using Internal.TypeSystem; -using Internal.Metadata.NativeFormat.Writer; - -using ILCompiler.Metadata; -using ILCompiler.DependencyAnalysis; - -using Debug = System.Diagnostics.Debug; - -namespace ILCompiler -{ - /// - /// Base class for metadata managers that generate metadata blobs. - /// - public abstract class GeneratingMetadataManager : MetadataManager - { - protected readonly string _metadataLogFile; - protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; - private readonly ModuleDesc _generatedAssembly; - - public GeneratingMetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, - ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, - DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, MetadataManagerOptions options) - : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, invokeThunkGenerationPolicy, options) - { - _metadataLogFile = logFile; - _stackTraceEmissionPolicy = stackTracePolicy; - _generatedAssembly = typeSystemContext.GeneratedAssembly; - } - - public sealed override bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) - { - return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; - } - - public sealed override bool WillUseMetadataTokenToReferenceField(FieldDesc field) - { - return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; - } - - protected void ComputeMetadata( - TPolicy policy, - NodeFactory factory, - out byte[] metadataBlob, - out List> typeMappings, - out List> methodMappings, - out List> fieldMappings, - out List stackTraceMapping) where TPolicy : struct, IMetadataPolicy - { - var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); - MetadataTransform transform = transformed.Transform; - - // Generate metadata blob - var writer = new MetadataWriter(); - writer.ScopeDefinitions.AddRange(transformed.Scopes); - - // Generate entries in the blob for methods that will be necessary for stack trace purposes. - var stackTraceRecords = new List(); - foreach (var methodBody in GetCompiledMethodBodies()) - { - MethodDesc method = methodBody.Method; - - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - - // Methods that will end up in the reflection invoke table should not have an entry in stack trace table - // We'll try looking them up in reflection data at runtime. - if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && - ShouldMethodBeInInvokeMap(method) && - (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) - continue; - - MethodStackTraceVisibilityFlags stackVisibility = _stackTraceEmissionPolicy.GetMethodVisibility(method); - bool isHidden = (stackVisibility & MethodStackTraceVisibilityFlags.IsHidden) != 0; - - if ((stackVisibility & MethodStackTraceVisibilityFlags.HasMetadata) != 0) - { - StackTraceRecordData record = CreateStackTraceRecord(transform, method, isHidden); - - stackTraceRecords.Add(record); - - writer.AdditionalRootRecords.Add(record.OwningType); - writer.AdditionalRootRecords.Add(record.MethodName); - writer.AdditionalRootRecords.Add(record.MethodSignature); - writer.AdditionalRootRecords.Add(record.MethodInstantiationArgumentCollection); - } - else if (isHidden) - { - stackTraceRecords.Add(new StackTraceRecordData(method, null, null, null, null, isHidden)); - } - } - - var ms = new MemoryStream(); - - // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. - var noThrowUtf8Encoding = new UTF8Encoding(false, false); - - using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) - { - writer.LogWriter = logWriter; - writer.Write(ms); - } - - metadataBlob = ms.ToArray(); - - const int MaxAllowedMetadataOffset = 0xFFFFFF; - if (metadataBlob.Length > MaxAllowedMetadataOffset) - { - // Offset portion of metadata handles is limited to 16 MB. - throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})"); - } - - typeMappings = new List>(); - methodMappings = new List>(); - fieldMappings = new List>(); - stackTraceMapping = new List(); - - // Generate type definition mappings - foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) - { - MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; - if (definition == null) - continue; - - MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); - - // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, - // if there is an MethodTable for it, we also need a mapping table entry for it. - record ??= transformed.GetTransformedTypeReference(definition); - - if (record != null) - typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); - } - - foreach (var method in GetReflectableMethods()) - { - if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) - { - // Generic definitions don't have runtime artifacts we would need to map to. - continue; - } - - if (method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) - { - // Methods that are not in their canonical form are not interesting - continue; - } - - if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) - continue; - - if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) - continue; - - MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); - - if (record != null) - methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); - } - - HashSet canonicalFields = new HashSet(); - foreach (var field in GetFieldsWithRuntimeMapping()) - { - FieldDesc fieldToAdd = field; - TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); - if (canonOwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType); - - // If we already added a canonically equivalent field, skip this one. - if (!canonicalFields.Add(canonField)) - continue; - - fieldToAdd = canonField; - } - - Field record = transformed.GetTransformedFieldDefinition(fieldToAdd.GetTypicalFieldDefinition()); - if (record != null) - fieldMappings.Add(new MetadataMapping(fieldToAdd, writer.GetRecordHandle(record))); - } - - // Generate stack trace metadata mapping - foreach (var stackTraceRecord in stackTraceRecords) - { - if (stackTraceRecord.OwningType != null) - { - StackTraceMapping mapping = new StackTraceMapping( - stackTraceRecord.Method, - writer.GetRecordHandle(stackTraceRecord.OwningType), - writer.GetRecordHandle(stackTraceRecord.MethodSignature), - writer.GetRecordHandle(stackTraceRecord.MethodName), - stackTraceRecord.MethodInstantiationArgumentCollection != null ? writer.GetRecordHandle(stackTraceRecord.MethodInstantiationArgumentCollection) : 0, - stackTraceRecord.IsHidden); - stackTraceMapping.Add(mapping); - } - else - { - Debug.Assert(stackTraceRecord.IsHidden); - stackTraceMapping.Add(new StackTraceMapping(stackTraceRecord.Method, 0, 0, 0, 0, stackTraceRecord.IsHidden)); - } - } - } - - /// - /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. - /// - /// - protected abstract IEnumerable GetFieldsWithRuntimeMapping(); - - /// - /// Gets a stub that can be used to reflection-invoke a method with a given signature. - /// - public sealed override MethodDesc GetReflectionInvokeStub(MethodDesc method) - { - return _typeSystemContext.GetDynamicInvokeThunk(method.Signature, - !method.Signature.IsStatic && method.OwningType.IsValueType); - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 5058524c8cecb4..2f92ccdb9048e7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -3,11 +3,14 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text; using Internal.TypeSystem; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Metadata; using Debug = System.Diagnostics.Debug; using ReadyToRunSectionType = Internal.Runtime.ReadyToRunSectionType; @@ -17,9 +20,12 @@ using CombinedDependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.CombinedDependencyListEntry; using MethodIL = Internal.IL.MethodIL; using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; +using MethodSignature = Internal.TypeSystem.MethodSignature; using MetadataRecord = Internal.Metadata.NativeFormat.Writer.MetadataRecord; +using MetadataWriter = Internal.Metadata.NativeFormat.Writer.MetadataWriter; using TypeReference = Internal.Metadata.NativeFormat.Writer.TypeReference; +using Field = Internal.Metadata.NativeFormat.Writer.Field; using TypeSpecification = Internal.Metadata.NativeFormat.Writer.TypeSpecification; using ConstantStringValue = Internal.Metadata.NativeFormat.Writer.ConstantStringValue; using TypeInstantiationSignature = Internal.Metadata.NativeFormat.Writer.TypeInstantiationSignature; @@ -43,6 +49,8 @@ public abstract class MetadataManager : ICompilationRootProvider private List> _fieldMappings; private List> _methodMappings; private List _stackTraceMappings; + protected readonly string _metadataLogFile; + protected readonly StackTraceEmissionPolicy _stackTraceEmissionPolicy; protected readonly CompilerTypeSystemContext _typeSystemContext; protected readonly MetadataBlockingPolicy _blockingPolicy; @@ -75,7 +83,8 @@ private readonly SortedSet _typeGVMEntries internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, - ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy, + ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, + DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy, MetadataManagerOptions options) { _typeSystemContext = typeSystemContext; @@ -83,6 +92,8 @@ public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBloc _resourceBlockingPolicy = resourceBlockingPolicy; _dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy; _options = options; + _metadataLogFile = logFile; + _stackTraceEmissionPolicy = stackTracePolicy; } public bool IsDataDehydrated => (_options & MetadataManagerOptions.DehydrateData) != 0; @@ -561,6 +572,12 @@ public virtual void GetDependenciesForOverridingMethod(ref CombinedDependencyLis { } + /// + /// Gets a list of fields that got "compiled" and are eligible for a runtime mapping. + /// + /// + protected abstract IEnumerable GetFieldsWithRuntimeMapping(); + /// /// This method is an extension point that can provide additional metadata-based dependencies to generated method bodies. /// @@ -601,18 +618,28 @@ public bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) /// Given that a method is invokable, if it is inserted into the reflection invoke table /// will it use a method token to be referenced, or not? /// - public abstract bool WillUseMetadataTokenToReferenceMethod(MethodDesc method); + public bool WillUseMetadataTokenToReferenceMethod(MethodDesc method) + { + return (GetMetadataCategory(method) & MetadataCategory.Description) != 0; + } /// /// Given that a method is invokable, if it is inserted into the reflection invoke table /// will it use a field token to be referenced, or not? /// - public abstract bool WillUseMetadataTokenToReferenceField(FieldDesc field); + public bool WillUseMetadataTokenToReferenceField(FieldDesc field) + { + return (GetMetadataCategory(field) & MetadataCategory.Description) != 0; + } /// /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// - public abstract MethodDesc GetReflectionInvokeStub(MethodDesc method); + public MethodDesc GetReflectionInvokeStub(MethodDesc method) + { + return _typeSystemContext.GetDynamicInvokeThunk(method.Signature, + !method.Signature.IsStatic && method.OwningType.IsValueType); + } protected void EnsureMetadataGenerated(NodeFactory factory) { @@ -635,6 +662,168 @@ protected abstract void ComputeMetadata(NodeFactory factory, out List> fieldMappings, out List stackTraceMapping); + protected void ComputeMetadata( + TPolicy policy, + NodeFactory factory, + out byte[] metadataBlob, + out List> typeMappings, + out List> methodMappings, + out List> fieldMappings, + out List stackTraceMapping) where TPolicy : struct, IMetadataPolicy + { + var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata()); + MetadataTransform transform = transformed.Transform; + + // Generate metadata blob + var writer = new MetadataWriter(); + writer.ScopeDefinitions.AddRange(transformed.Scopes); + + // Generate entries in the blob for methods that will be necessary for stack trace purposes. + var stackTraceRecords = new List(); + foreach (var methodBody in GetCompiledMethodBodies()) + { + MethodDesc method = methodBody.Method; + + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + + // Methods that will end up in the reflection invoke table should not have an entry in stack trace table + // We'll try looking them up in reflection data at runtime. + if (transformed.GetTransformedMethodDefinition(typicalMethod) != null && + ShouldMethodBeInInvokeMap(method) && + (GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) != 0) + continue; + + MethodStackTraceVisibilityFlags stackVisibility = _stackTraceEmissionPolicy.GetMethodVisibility(method); + bool isHidden = (stackVisibility & MethodStackTraceVisibilityFlags.IsHidden) != 0; + + if ((stackVisibility & MethodStackTraceVisibilityFlags.HasMetadata) != 0) + { + StackTraceRecordData record = CreateStackTraceRecord(transform, method, isHidden); + + stackTraceRecords.Add(record); + + writer.AdditionalRootRecords.Add(record.OwningType); + writer.AdditionalRootRecords.Add(record.MethodName); + writer.AdditionalRootRecords.Add(record.MethodSignature); + writer.AdditionalRootRecords.Add(record.MethodInstantiationArgumentCollection); + } + else if (isHidden) + { + stackTraceRecords.Add(new StackTraceRecordData(method, null, null, null, null, isHidden)); + } + } + + var ms = new MemoryStream(); + + // .NET metadata is UTF-16 and UTF-16 contains code points that don't translate to UTF-8. + var noThrowUtf8Encoding = new UTF8Encoding(false, false); + + using (var logWriter = _metadataLogFile != null ? new StreamWriter(File.Open(_metadataLogFile, FileMode.Create, FileAccess.Write, FileShare.Read), noThrowUtf8Encoding) : null) + { + writer.LogWriter = logWriter; + writer.Write(ms); + } + + metadataBlob = ms.ToArray(); + + const int MaxAllowedMetadataOffset = 0xFFFFFF; + if (metadataBlob.Length > MaxAllowedMetadataOffset) + { + // Offset portion of metadata handles is limited to 16 MB. + throw new InvalidOperationException($"Metadata blob exceeded the addressing range (allowed: {MaxAllowedMetadataOffset}, actual: {metadataBlob.Length})"); + } + + typeMappings = new List>(); + methodMappings = new List>(); + fieldMappings = new List>(); + stackTraceMapping = new List(); + + // Generate type definition mappings + foreach (var type in factory.MetadataManager.GetTypesWithEETypes()) + { + MetadataType definition = type.IsTypeDefinition ? type as MetadataType : null; + if (definition == null) + continue; + + MetadataRecord record = transformed.GetTransformedTypeDefinition(definition); + + // Reflection requires that we maintain type identity. Even if we only generated a TypeReference record, + // if there is an MethodTable for it, we also need a mapping table entry for it. + record ??= transformed.GetTransformedTypeReference(definition); + + if (record != null) + typeMappings.Add(new MetadataMapping(definition, writer.GetRecordHandle(record))); + } + + foreach (var method in GetReflectableMethods()) + { + if (method.IsGenericMethodDefinition || method.OwningType.IsGenericDefinition) + { + // Generic definitions don't have runtime artifacts we would need to map to. + continue; + } + + if (method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) + { + // Methods that are not in their canonical form are not interesting + continue; + } + + if (IsReflectionBlocked(method.Instantiation) || IsReflectionBlocked(method.OwningType.Instantiation)) + continue; + + if ((GetMetadataCategory(method) & MetadataCategory.RuntimeMapping) == 0) + continue; + + MetadataRecord record = transformed.GetTransformedMethodDefinition(method.GetTypicalMethodDefinition()); + + if (record != null) + methodMappings.Add(new MetadataMapping(method, writer.GetRecordHandle(record))); + } + + HashSet canonicalFields = new HashSet(); + foreach (var field in GetFieldsWithRuntimeMapping()) + { + FieldDesc fieldToAdd = field; + TypeDesc canonOwningType = field.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific); + if (canonOwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + FieldDesc canonField = _typeSystemContext.GetFieldForInstantiatedType(field.GetTypicalFieldDefinition(), (InstantiatedType)canonOwningType); + + // If we already added a canonically equivalent field, skip this one. + if (!canonicalFields.Add(canonField)) + continue; + + fieldToAdd = canonField; + } + + Field record = transformed.GetTransformedFieldDefinition(fieldToAdd.GetTypicalFieldDefinition()); + if (record != null) + fieldMappings.Add(new MetadataMapping(fieldToAdd, writer.GetRecordHandle(record))); + } + + // Generate stack trace metadata mapping + foreach (var stackTraceRecord in stackTraceRecords) + { + if (stackTraceRecord.OwningType != null) + { + StackTraceMapping mapping = new StackTraceMapping( + stackTraceRecord.Method, + writer.GetRecordHandle(stackTraceRecord.OwningType), + writer.GetRecordHandle(stackTraceRecord.MethodSignature), + writer.GetRecordHandle(stackTraceRecord.MethodName), + stackTraceRecord.MethodInstantiationArgumentCollection != null ? writer.GetRecordHandle(stackTraceRecord.MethodInstantiationArgumentCollection) : 0, + stackTraceRecord.IsHidden); + stackTraceMapping.Add(mapping); + } + else + { + Debug.Assert(stackTraceRecord.IsHidden); + stackTraceMapping.Add(new StackTraceMapping(stackTraceRecord.Method, 0, 0, 0, 0, stackTraceRecord.IsHidden)); + } + } + } + protected StackTraceRecordData CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method, bool isHidden) { // In the metadata, we only represent the generic definition diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 1f798698ba1514..2b823f42465108 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -34,7 +34,7 @@ namespace ILCompiler /// This class is responsible for managing native metadata to be emitted into the compiled /// module. It applies a policy that every type/method that is statically used shall be reflectable. /// - public sealed class UsageBasedMetadataManager : GeneratingMetadataManager + public sealed class UsageBasedMetadataManager : MetadataManager { private readonly CompilationModuleGroup _compilationModuleGroup; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 1137962a793221..6a35c1cc5c9063 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -455,7 +455,6 @@ - From a7b63ae925d7aa8c89039636b9fef2d5512a2642 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 18 Jan 2024 10:45:02 -0500 Subject: [PATCH 098/189] Add capacity hint to OidCollection (#97106) --- .../System/Security/Cryptography/OidCollection.cs | 15 ++++++++++++--- .../X509Certificates/OpenSslX509Encoder.cs | 3 ++- .../X509EnhancedKeyUsageExtension.cs | 9 +++++++-- .../X509Pal.Windows.CustomExtensions.cs | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/OidCollection.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/OidCollection.cs index be5d304e9106e0..724045a8086fd4 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/OidCollection.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/OidCollection.cs @@ -2,16 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; -using Internal.Cryptography; +using System.Diagnostics; namespace System.Security.Cryptography { public sealed class OidCollection : ICollection { - private Oid[] _oids = Array.Empty(); + private Oid[] _oids; private int _count; - public OidCollection() { } + public OidCollection() + { + _oids = []; + } + + internal OidCollection(int initialCapacity) + { + Debug.Assert(initialCapacity >= 0); + _oids = initialCapacity == 0 ? [] : new Oid[initialCapacity]; + } public int Add(Oid oid) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs index 529861fee448db..4d43399e6f7c5e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509Encoder.cs @@ -235,13 +235,14 @@ public override void DecodeX509BasicConstraints2Extension( public override void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages) { - OidCollection oids = new OidCollection(); + OidCollection oids; using (SafeEkuExtensionHandle eku = Interop.Crypto.DecodeExtendedKeyUsage(encoded, encoded.Length)) { Interop.Crypto.CheckValidOpenSslHandle(eku); int count = Interop.Crypto.GetX509EkuFieldCount(eku); + oids = new OidCollection(count); for (int i = 0; i < count; i++) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509EnhancedKeyUsageExtension.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509EnhancedKeyUsageExtension.cs index 712e2dfb0d0311..1c26fe1d5cf64c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509EnhancedKeyUsageExtension.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509EnhancedKeyUsageExtension.cs @@ -31,9 +31,14 @@ public OidCollection EnhancedKeyUsages X509Pal.Instance.DecodeX509EnhancedKeyUsageExtension(RawData, out _enhancedKeyUsages); _decoded = true; } - OidCollection oids = new OidCollection(); - foreach (Oid oid in _enhancedKeyUsages!) + + OidCollection oids = new OidCollection(_enhancedKeyUsages!.Count); + + foreach (Oid oid in _enhancedKeyUsages) + { oids.Add(oid); + } + return oids; } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Windows.CustomExtensions.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Windows.CustomExtensions.cs index c990d994009686..f0a2f8a489d0f1 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Windows.CustomExtensions.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Pal.Windows.CustomExtensions.cs @@ -140,11 +140,11 @@ public void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollectio CryptDecodeObjectStructType.X509_ENHANCED_KEY_USAGE, static delegate (void* pvDecoded, int cbDecoded) { - var localUsages = new OidCollection(); Debug.Assert(cbDecoded >= sizeof(CERT_ENHKEY_USAGE)); CERT_ENHKEY_USAGE* pEnhKeyUsage = (CERT_ENHKEY_USAGE*)pvDecoded; int count = pEnhKeyUsage->cUsageIdentifier; + var localUsages = new OidCollection(count); for (int i = 0; i < count; i++) { IntPtr oidValuePointer = pEnhKeyUsage->rgpszUsageIdentifier[i]; From 02a3fcb60a1e59e0193ef3f6c70f90d59c436abc Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Thu, 18 Jan 2024 09:22:27 -0800 Subject: [PATCH 099/189] Fix maintenance of `genReturnBB` pointer (#96935) If the `genReturnBB` block is split, the pointer needs to be updated. Without this, we ended up with a situation where the `genReturnBB` did not point to the return block, leading to omitting the code to remove the PInvoke frame from the thread's Frame list. Fixes #96409 --- src/coreclr/jit/compiler.h | 2 ++ src/coreclr/jit/fgbasic.cpp | 20 ++++++++++++++++++++ src/coreclr/jit/fgdiagnostic.cpp | 7 +++++++ 3 files changed, 29 insertions(+) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 2d15aab8a96560..80e0dc70809c78 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5348,6 +5348,8 @@ class Compiler IL_OFFSET fgFindBlockILOffset(BasicBlock* block); void fgFixEntryFlowForOSR(); + void fgUpdateSingleReturnBlock(BasicBlock* block); + BasicBlock* fgSplitBlockAtBeginning(BasicBlock* curr); BasicBlock* fgSplitBlockAtEnd(BasicBlock* curr); BasicBlock* fgSplitBlockAfterStatement(BasicBlock* curr, Statement* stmt); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index d88ed59a8f1963..a7a7cc0b5d980d 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -4755,6 +4755,24 @@ IL_OFFSET Compiler::fgFindBlockILOffset(BasicBlock* block) return BAD_IL_OFFSET; } +//------------------------------------------------------------------------------ +// fgUpdateSingleReturnBlock : A block has been split. If it was the single return +// block, then update the single return block pointer. +// +// Arguments: +// block - The block that was split +// +void Compiler::fgUpdateSingleReturnBlock(BasicBlock* block) +{ + assert(block->KindIs(BBJ_ALWAYS)); + if (genReturnBB == block) + { + assert(block->GetTarget()->KindIs(BBJ_RETURN)); + JITDUMP("Updating genReturnBB from " FMT_BB " to " FMT_BB "\n", block->bbNum, block->GetTarget()->bbNum); + genReturnBB = block->GetTarget(); + } +} + //------------------------------------------------------------------------------ // fgSplitBlockAtEnd - split the given block into two blocks. // All code in the block stays in the original block. @@ -4831,6 +4849,8 @@ BasicBlock* Compiler::fgSplitBlockAtEnd(BasicBlock* curr) fgAddRefPred(newBlock, curr); + fgUpdateSingleReturnBlock(curr); + return newBlock; } diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 2fbf905d131acf..1b8ed455ae5a67 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -2255,6 +2255,12 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 * } } + // Indicate if it's the single return block + if (block == genReturnBB) + { + printf(" one-return"); + } + printf("\n"); } @@ -3186,6 +3192,7 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef if (genReturnBB != nullptr) { assert(genReturnBB->GetFirstLIRNode() != nullptr || genReturnBB->bbStmtList != nullptr); + assert(genReturnBB->KindIs(BBJ_RETURN)); } // If this is an inlinee, we're done checking. From 21ecb726f9d60fecbc37f1037f4bb5007e7a7c99 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 18 Jan 2024 19:32:41 +0200 Subject: [PATCH 100/189] [mono][interp] Fix some leaks during compilation (#97143) * [mono][interp] Stop trying to inflate signature The target method and, implicitly, its signature are already inflated. This was allocating a new signature every time the method was called which was leaked. On some of the bigger tests suites, that heavily use generics, this can reduce the mem usage in the order of GBs. * [mono][interp] Free mheader in case of inline failure The header local types are not used anywhere so we can just free it. --- src/mono/mono/mini/interp/transform.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 5f9a612b1a6cdc..464ddebd676132 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3103,6 +3103,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi int dreg, this_reg = -1; int prev_sp_offset; MonoClass *klass = target_method->klass; + MonoMethodHeader *mheader = NULL; if (!(mono_interp_opt & INTERP_OPT_INLINE) || !interp_method_check_inlining (td, target_method, csignature)) @@ -3166,7 +3167,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi if (is_protected) newobj_fast->flags |= INTERP_INST_FLAG_PROTECTED_NEWOBJ; - MonoMethodHeader *mheader = interp_method_get_header (target_method, error); + mheader = interp_method_get_header (target_method, error); goto_if_nok (error, fail); if (!interp_inline_method (td, target_method, mheader, error)) @@ -3180,6 +3181,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi push_var (td, dreg); return TRUE; fail: + mono_metadata_free_mh (mheader); // Restore the state td->sp = td->stack + prev_sp_offset; td->last_ins = prev_last_ins; @@ -3214,10 +3216,14 @@ interp_constrained_box (TransformData *td, MonoClass *constrained_class, MonoMet static MonoMethod* interp_get_method (MonoMethod *method, guint32 token, MonoImage *image, MonoGenericContext *generic_context, MonoError *error) { - if (method->wrapper_type == MONO_WRAPPER_NONE) + if (method->wrapper_type == MONO_WRAPPER_NONE) { return mono_get_method_checked (image, token, NULL, generic_context, error); - else - return (MonoMethod *)mono_method_get_wrapper_data (method, token); + } else { + MonoMethod *target_method = mono_method_get_wrapper_data (method, token); + if (generic_context) + target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error); + return target_method; + } } /* @@ -3440,13 +3446,6 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target target_method = interp_get_method (method, token, image, generic_context, error); return_val_if_nok (error, FALSE); csignature = mono_method_signature_internal (target_method); - - if (generic_context) { - csignature = mono_inflate_generic_signature (csignature, generic_context, error); - return_val_if_nok (error, FALSE); - target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, error); - return_val_if_nok (error, FALSE); - } } } else { csignature = mono_method_signature_internal (target_method); @@ -3654,6 +3653,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target td->ip += 5; goto done; } + mono_metadata_free_mh (mheader); } /* From 72dc71d7cca722a651e808abee84ae82f0bee904 Mon Sep 17 00:00:00 2001 From: Alan Hayward Date: Thu, 18 Jan 2024 17:47:04 +0000 Subject: [PATCH 101/189] Add Arm64 encodings for IF_SVE_AT_3A to IF_SVE_BK_3A (#97082) --- src/coreclr/jit/codegenarm64test.cpp | 46 +++++++ src/coreclr/jit/emitarm64.cpp | 193 +++++++++++++++++++++++++-- src/coreclr/jit/instr.h | 4 + 3 files changed, 234 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 45afd2629a3682..03edaae7366903 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5092,6 +5092,52 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_R(INS_sve_msb, EA_SCALABLE, REG_V8, REG_P7, REG_V12, REG_V22, INS_OPTS_SCALABLE_D); // MSB ., /M, ., . + // IF_SVE_AT_3A + theEmitter->emitIns_R_R_R(INS_sve_add, EA_SCALABLE, REG_V0, REG_V0, REG_V0, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_UNPREDICATED); // ADD ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sqadd, EA_SCALABLE, REG_V3, REG_V31, REG_V12, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_UNPREDICATED); // SQADD ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sqsub, EA_SCALABLE, REG_V7, REG_V0, REG_V31, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_UNPREDICATED); // SQSUB ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sub, EA_SCALABLE, REG_V19, REG_V7, REG_V13, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_UNPREDICATED); // SUB ., ., . + theEmitter->emitIns_R_R_R(INS_sve_uqadd, EA_SCALABLE, REG_V23, REG_V28, REG_V29, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_UNPREDICATED); // UQADD ., ., . + theEmitter->emitIns_R_R_R(INS_sve_uqsub, EA_SCALABLE, REG_V31, REG_V31, REG_V31, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_UNPREDICATED); // UQSUB ., ., . + + // IF_SVE_BA_3A + theEmitter->emitIns_R_R_R(INS_sve_index, EA_4BYTE, REG_V24, REG_ZR, REG_R9, + INS_OPTS_SCALABLE_B); // INDEX ., , + theEmitter->emitIns_R_R_R(INS_sve_index, EA_8BYTE, REG_V12, REG_R15, REG_R0, + INS_OPTS_SCALABLE_D); // INDEX ., , + + // IF_SVE_BD_3A + theEmitter->emitIns_R_R_R(INS_sve_mul, EA_SCALABLE, REG_V5, REG_V0, REG_V31, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_UNPREDICATED); // MUL ., ., . + theEmitter->emitIns_R_R_R(INS_sve_smulh, EA_SCALABLE, REG_V0, REG_V31, REG_V5, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_UNPREDICATED); // SMULH ., ., . + theEmitter->emitIns_R_R_R(INS_sve_umulh, EA_SCALABLE, REG_V31, REG_V5, REG_V0, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_UNPREDICATED); // UMULH ., ., . + + // IF_SVE_BE_3A + theEmitter->emitIns_R_R_R(INS_sve_sqdmulh, EA_SCALABLE, REG_V7, REG_V28, REG_V0, + INS_OPTS_SCALABLE_B); // SQDMULH ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sqrdmulh, EA_SCALABLE, REG_V23, REG_V3, REG_V31, + INS_OPTS_SCALABLE_H); // SQRDMULH ., ., . + + // IF_SVE_BG_3A + theEmitter->emitIns_R_R_R(INS_sve_asr, EA_SCALABLE, REG_V9, REG_V31, REG_V2, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_UNPREDICATED_WIDE); // ASR ., ., .D + theEmitter->emitIns_R_R_R(INS_sve_lsl, EA_SCALABLE, REG_V19, REG_V0, REG_V12, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_UNPREDICATED_WIDE); // LSL ., ., .D + theEmitter->emitIns_R_R_R(INS_sve_lsr, EA_SCALABLE, REG_V29, REG_V10, REG_V22, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_UNPREDICATED_WIDE); // LSR ., ., .D + + // IF_SVE_BK_3A + theEmitter->emitIns_R_R_R(INS_sve_ftssel, EA_SCALABLE, REG_V17, REG_V16, REG_V15, + INS_OPTS_SCALABLE_D); // FTSSEL ., ., . + // IF_SVE_CL_3A theEmitter->emitIns_R_R_R(INS_sve_compact, EA_SCALABLE, REG_V16, REG_P7, REG_V13, INS_OPTS_SCALABLE_S); // COMPACT ., , . diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index ce5a2820e0e43b..1877f0a9036e9f 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1055,6 +1055,32 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isScalableVectorSize(elemsize)); break; + // Scalable, unpredicated + case IF_SVE_AT_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE integer add/subtract vectors (unpredicated) + case IF_SVE_BD_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 integer multiply vectors (unpredicated) + case IF_SVE_BE_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 signed saturating doubling multiply high + // (unpredicated) + case IF_SVE_BG_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE bitwise shift by wide elements (unpredicated) + case IF_SVE_BK_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE floating-point trig select coefficient + elemsize = id->idOpSize(); + assert(insOptsScalableStandard(id->idInsOpt())); // xx + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isVectorRegister(id->idReg2())); // nnnnn + assert(isVectorRegister(id->idReg3())); // mmmmm + assert(isScalableVectorSize(elemsize)); + break; + + // Scalable, no predicates. General purpose source registers + case IF_SVE_BA_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE index generation (register start, register + // increment) + elemsize = id->idOpSize(); + assert(insOptsScalableStandard(id->idInsOpt())); // xx + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isGeneralRegisterOrZR(id->idReg2())); // nnnnn + assert(isGeneralRegisterOrZR(id->idReg3())); // mmmmm + assert(isValidScalarDatasize(elemsize)); + break; + // Scalable, 4 regs, to predicate register. case IF_SVE_CX_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE integer compare vectors elemsize = id->idOpSize(); @@ -8860,11 +8886,20 @@ void emitter::emitIns_R_R_R(instruction ins, case INS_sve_sub: case INS_sve_subr: assert(isVectorRegister(reg1)); - assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); assert(insOptsScalableStandard(opt)); - assert(insScalableOptsNone(sopt)); - fmt = IF_SVE_AB_3A; + if (sopt == INS_SCALABLE_OPTS_UNPREDICATED) + { + assert(isVectorRegister(reg2)); + assert(ins != INS_sve_subr); + fmt = IF_SVE_AT_3A; + } + else + { + assert(isLowPredicateRegister(reg2)); + assert(insScalableOptsNone(sopt)); + fmt = IF_SVE_AB_3A; + } break; case INS_sve_sdiv: @@ -8897,11 +8932,19 @@ void emitter::emitIns_R_R_R(instruction ins, case INS_sve_smulh: case INS_sve_umulh: assert(isVectorRegister(reg1)); - assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); assert(insOptsScalableStandard(opt)); - assert(insScalableOptsNone(sopt)); - fmt = IF_SVE_AE_3A; + if (sopt == INS_SCALABLE_OPTS_UNPREDICATED) + { + assert(isVectorRegister(reg2)); + fmt = IF_SVE_BD_3A; + } + else + { + assert(insScalableOptsNone(sopt)); + assert(isLowPredicateRegister(reg2)); + fmt = IF_SVE_AE_3A; + } break; case INS_sve_andv: @@ -8999,15 +9042,22 @@ void emitter::emitIns_R_R_R(instruction ins, case INS_sve_lsl: case INS_sve_lsr: assert(isVectorRegister(reg1)); - assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); if (sopt == INS_SCALABLE_OPTS_WIDE) { + assert(isLowPredicateRegister(reg2)); assert(insOptsScalableWide(opt)); fmt = IF_SVE_AO_3A; } + else if (sopt == INS_SCALABLE_OPTS_UNPREDICATED_WIDE) + { + assert(isVectorRegister(reg2)); + assert(insOptsScalableWide(opt)); + fmt = IF_SVE_BG_3A; + } else { + assert(isLowPredicateRegister(reg2)); assert(insScalableOptsNone(sopt)); assert(insOptsScalableStandard(opt)); fmt = IF_SVE_AN_3A; @@ -9077,6 +9127,37 @@ void emitter::emitIns_R_R_R(instruction ins, fmt = IF_SVE_AQ_3A; break; + case INS_sve_index: + assert(isValidScalarDatasize(size)); + assert(isVectorRegister(reg1)); + assert(isGeneralRegisterOrZR(reg2)); + assert(isGeneralRegisterOrZR(reg3)); + assert(insOptsScalableStandard(opt)); + assert(insScalableOptsNone(sopt)); + fmt = IF_SVE_BA_3A; + break; + + case INS_sve_sqdmulh: + case INS_sve_sqrdmulh: + assert(isScalableVectorSize(size)); + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(insOptsScalableStandard(opt)); + assert(insScalableOptsNone(sopt)); + fmt = IF_SVE_BE_3A; + break; + + case INS_sve_ftssel: + assert(isScalableVectorSize(size)); + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(insOptsScalableFloat(opt)); + assert(insScalableOptsNone(sopt)); + fmt = IF_SVE_BK_3A; + break; + case INS_sve_compact: assert(isVectorRegister(reg1)); assert(isLowPredicateRegister(reg2)); @@ -9248,10 +9329,27 @@ void emitter::emitIns_R_R_R(instruction ins, case INS_sve_sqadd: case INS_sve_sqsub: - case INS_sve_sqsubr: - case INS_sve_suqadd: case INS_sve_uqadd: case INS_sve_uqsub: + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg3)); + assert(insOptsScalableStandard(opt)); + assert(isScalableVectorSize(size)); + if (sopt == INS_SCALABLE_OPTS_UNPREDICATED) + { + assert(isVectorRegister(reg2)); + fmt = IF_SVE_AT_3A; + } + else + { + assert(insScalableOptsNone(sopt)); + assert(isLowPredicateRegister(reg2)); + fmt = IF_SVE_ET_3A; + } + break; + + case INS_sve_sqsubr: + case INS_sve_suqadd: case INS_sve_uqsubr: case INS_sve_usqadd: assert(isVectorRegister(reg1)); @@ -9259,6 +9357,7 @@ void emitter::emitIns_R_R_R(instruction ins, assert(isVectorRegister(reg3)); assert(insOptsScalableStandard(opt)); assert(insScalableOptsNone(sopt)); + assert(isScalableVectorSize(size)); fmt = IF_SVE_ET_3A; break; @@ -16477,6 +16576,32 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + // Scalable, 3 regs, no predicates + case IF_SVE_AT_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE integer add/subtract vectors (unpredicated) + case IF_SVE_BD_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 integer multiply vectors (unpredicated) + case IF_SVE_BE_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 signed saturating doubling multiply high + // (unpredicated) + case IF_SVE_BG_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE bitwise shift by wide elements (unpredicated) + case IF_SVE_BK_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE floating-point trig select coefficient + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_V_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeReg_V_20_to_16(id->idReg3()); // mmmmm + code |= insEncodeSveElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + + // Scalable, 3 regs, no predicates. General purpose source registers + case IF_SVE_BA_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE index generation (register start, register + // increment) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_Rn(id->idReg2()); // nnnnn + code |= insEncodeReg_Rm(id->idReg3()); // mmmmm + code |= insEncodeSveElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + // Scalable to general register. case IF_SVE_CO_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally extract element to general register case IF_SVE_CS_3A: // ........xx...... ...gggnnnnnddddd -- SVE extract element to general register @@ -19107,6 +19232,32 @@ void emitter::emitDispInsHelp( emitDispSveReg(id->idReg4(), id->idInsOpt(), false); break; + // ., ., . + case IF_SVE_AT_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE integer add/subtract vectors (unpredicated) + case IF_SVE_BD_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 integer multiply vectors (unpredicated) + case IF_SVE_BE_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 signed saturating doubling multiply high + // (unpredicated) + case IF_SVE_BK_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE floating-point trig select coefficient + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg2(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + break; + + // ., , + case IF_SVE_BA_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE index generation (register start, register + // increment) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispReg(id->idReg2(), size, true); // nnnnn + emitDispReg(id->idReg3(), size, false); // mmmmm + break; + + // ., ., .D + case IF_SVE_BG_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE bitwise shift by wide elements (unpredicated) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg2(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg3(), INS_OPTS_SCALABLE_D, false); // mmmmm + break; + // ., , ., . case IF_SVE_CM_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally broadcast element to vector emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd @@ -21758,10 +21909,34 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins // (predicated) case IF_SVE_AS_4A: // ........xx.mmmmm ...gggaaaaaddddd -- SVE integer multiply-add writing multiplicand // (predicated) + case IF_SVE_BD_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 integer multiply vectors (unpredicated) + case IF_SVE_BE_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE2 signed saturating doubling multiply high + // (unpredicated) result.insThroughput = PERFSCORE_THROUGHPUT_2X; result.insLatency = PERFSCORE_LATENCY_5C; break; + case IF_SVE_AT_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE integer add/subtract vectors (unpredicated) + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + + case IF_SVE_BA_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE index generation (register start, register + // increment) + result.insThroughput = PERFSCORE_THROUGHPUT_2X; + result.insLatency = PERFSCORE_LATENCY_8C; + break; + + case IF_SVE_BK_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE floating-point trig select coefficient + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_3C; + break; + + case IF_SVE_BG_3A: // ........xx.mmmmm ......nnnnnddddd -- SVE bitwise shift by wide elements (unpredicated) + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + // Conditional extract operations, SIMD&FP scalar and vector forms case IF_SVE_CL_3A: // ........xx...... ...gggnnnnnddddd -- SVE compress active elements case IF_SVE_CM_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally broadcast element to vector diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index a667cf938b01c0..5fd9dd456d65cd 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -316,6 +316,10 @@ enum insScalableOpts : unsigned INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR, // Variants with {., .} predicate pair (eg whilege) INS_SCALABLE_OPTS_VL_2X, // Variants with a vector length specifier of 2x (eg whilege) INS_SCALABLE_OPTS_VL_4X, // Variants with a vector length specifier of 4x (eg whilege) + + // Removable once REG_V0 and REG_P0 are distinct + INS_SCALABLE_OPTS_UNPREDICATED, // Variants without a predicate (eg add) + INS_SCALABLE_OPTS_UNPREDICATED_WIDE, // Variants without a predicate and wide elements (eg asr) }; enum insCond : unsigned From 5415bd20172736cde7c822d4168dbf804f898088 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 18 Jan 2024 10:41:35 -0800 Subject: [PATCH 102/189] Fix compareTypesForEquality (#97062) Fixes #96876 --- .../tools/Common/Compiler/TypeExtensions.cs | 73 +++++++++++------ src/coreclr/vm/jitinterface.cpp | 79 ++++++++++++++----- .../InteropServices/CollectionsMarshal.cs | 3 +- .../coreclr/GitHub_96876/test96876.cs | 26 ++++++ .../coreclr/GitHub_96876/test96876.csproj | 11 +++ 5 files changed, 145 insertions(+), 47 deletions(-) create mode 100644 src/tests/Regressions/coreclr/GitHub_96876/test96876.cs create mode 100644 src/tests/Regressions/coreclr/GitHub_96876/test96876.csproj diff --git a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs index 04e8e65f61f3b1..f16b643e0cbb41 100644 --- a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs +++ b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs @@ -182,43 +182,68 @@ public static bool IsArrayTypeWithoutGenericInterfaces(this TypeDesc type) public static bool? CompareTypesForEquality(TypeDesc type1, TypeDesc type2) { - bool? result = null; - // If neither type is a canonical subtype, type handle comparison suffices if (!type1.IsCanonicalSubtype(CanonicalFormKind.Any) && !type2.IsCanonicalSubtype(CanonicalFormKind.Any)) { - result = type1 == type2; + return type1 == type2; } + // If either or both types are canonical subtypes, we can sometimes prove inequality. - else + if (AreGuaranteedToRepresentDifferentTypes(type1, type2)) + { + return false; + } + + return null; + + static bool AreGuaranteedToRepresentDifferentTypes(TypeDesc type1, TypeDesc type2) { - // If either is a value type then the types cannot - // be equal unless the type defs are the same. - if (type1.IsValueType || type2.IsValueType) + if (type1.IsCanonicalDefinitionType(CanonicalFormKind.Any) || type2.IsCanonicalDefinitionType(CanonicalFormKind.Any)) { - if (!type1.IsCanonicalDefinitionType(CanonicalFormKind.Universal) && !type2.IsCanonicalDefinitionType(CanonicalFormKind.Universal)) - { - if (!type1.HasSameTypeDefinition(type2)) - { - result = false; - } - } + // Universal canonical definition can match any type. We can't prove inequality. + if (type1.IsCanonicalDefinitionType(CanonicalFormKind.Universal) || type2.IsCanonicalDefinitionType(CanonicalFormKind.Universal)) + return false; + + return type1.IsGCPointer != type2.IsGCPointer; } - // If we have two ref types that are not __Canon, then the - // types cannot be equal unless the type defs are the same. - else + + TypeFlags category = type1.Category; + if (category != type2.Category) + return true; + + switch (category) { - if (!type1.IsCanonicalDefinitionType(CanonicalFormKind.Any) && !type2.IsCanonicalDefinitionType(CanonicalFormKind.Any)) - { - if (!type1.HasSameTypeDefinition(type2)) + case TypeFlags.Array: + if (((ArrayType)type1).Rank != ((ArrayType)type2).Rank) + return true; + return AreGuaranteedToRepresentDifferentTypes(((ArrayType)type1).ElementType, ((ArrayType)type2).ElementType); + case TypeFlags.SzArray: + case TypeFlags.ByRef: + case TypeFlags.Pointer: + return AreGuaranteedToRepresentDifferentTypes(((ParameterizedType)type1).ParameterType, ((ParameterizedType)type2).ParameterType); + + default: + if (type1.IsDefType || type2.IsDefType) { - result = false; + if (!type1.HasSameTypeDefinition(type2)) + return true; + + Instantiation inst1 = type1.Instantiation; + if (inst1.Length != 0) + { + var inst2 = type2.Instantiation; + Debug.Assert(inst1.Length == inst2.Length); + for (int i = 0; i < inst1.Length; i++) + { + if (AreGuaranteedToRepresentDifferentTypes(inst1[i], inst2[i])) + return true; + } + } } - } + break; } + return false; } - - return result; } public static TypeDesc MergeTypesToCommonParent(TypeDesc ta, TypeDesc tb) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 816b393ec89de2..c6b6af6fc6e1a2 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -4448,6 +4448,61 @@ TypeCompareState CEEInfo::compareTypesForCast( return result; } +// Returns true if hnd1 and hnd2 are guaranteed to represent different types. Returns false if hnd1 and hnd2 may represent the same type. +static bool AreGuaranteedToRepresentDifferentTypes(TypeHandle hnd1, TypeHandle hnd2) +{ + STANDARD_VM_CONTRACT; + + CorElementType et1 = hnd1.GetSignatureCorElementType(); + CorElementType et2 = hnd2.GetSignatureCorElementType(); + + TypeHandle canonHnd = TypeHandle(g_pCanonMethodTableClass); + if ((hnd1 == canonHnd) || (hnd2 == canonHnd)) + { + return CorTypeInfo::IsObjRef(et1) != CorTypeInfo::IsObjRef(et2); + } + + if (et1 != et2) + { + return true; + } + + switch (et1) + { + case ELEMENT_TYPE_ARRAY: + if (hnd1.GetRank() != hnd2.GetRank()) + return true; + return AreGuaranteedToRepresentDifferentTypes(hnd1.GetTypeParam(), hnd2.GetTypeParam()); + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_BYREF: + case ELEMENT_TYPE_PTR: + return AreGuaranteedToRepresentDifferentTypes(hnd1.GetTypeParam(), hnd2.GetTypeParam()); + + default: + if (!hnd1.IsTypeDesc()) + { + if (!hnd1.AsMethodTable()->HasSameTypeDefAs(hnd2.AsMethodTable())) + return true; + + if (hnd1.AsMethodTable()->HasInstantiation()) + { + Instantiation inst1 = hnd1.AsMethodTable()->GetInstantiation(); + Instantiation inst2 = hnd2.AsMethodTable()->GetInstantiation(); + _ASSERTE(inst1.GetNumArgs() == inst2.GetNumArgs()); + + for (DWORD i = 0; i < inst1.GetNumArgs(); i++) + { + if (AreGuaranteedToRepresentDifferentTypes(inst1[i], inst2[i])) + return true; + } + } + } + break; + } + + return false; +} + /*********************************************************************/ // See if types represented by cls1 and cls2 compare equal, not // equal, or the comparison needs to be resolved at runtime. @@ -4473,30 +4528,12 @@ TypeCompareState CEEInfo::compareTypesForEquality( { result = (hnd1 == hnd2 ? TypeCompareState::Must : TypeCompareState::MustNot); } - // If either or both types are canonical subtypes, we can sometimes prove inequality. else { - // If either is a value type then the types cannot - // be equal unless the type defs are the same. - if (hnd1.IsValueType() || hnd2.IsValueType()) + // If either or both types are canonical subtypes, we can sometimes prove inequality. + if (AreGuaranteedToRepresentDifferentTypes(hnd1, hnd2)) { - if (!hnd1.GetMethodTable()->HasSameTypeDefAs(hnd2.GetMethodTable())) - { - result = TypeCompareState::MustNot; - } - } - // If we have two ref types that are not __Canon, then the - // types cannot be equal unless the type defs are the same. - else - { - TypeHandle canonHnd = TypeHandle(g_pCanonMethodTableClass); - if ((hnd1 != canonHnd) && (hnd2 != canonHnd)) - { - if (!hnd1.GetMethodTable()->HasSameTypeDefAs(hnd2.GetMethodTable())) - { - result = TypeCompareState::MustNot; - } - } + result = TypeCompareState::MustNot; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs index ec3d0f08c8e1e7..e3cecd684abf6a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs @@ -34,8 +34,7 @@ public static Span AsSpan(List? list) ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); } - // Commented out to workaround https://github.com/dotnet/runtime/issues/96876 - // Debug.Assert(typeof(T[]) == list._items.GetType(), "Implementation depends on List always using a T[] and not U[] where U : T."); + Debug.Assert(typeof(T[]) == list._items.GetType(), "Implementation depends on List always using a T[] and not U[] where U : T."); span = new Span(ref MemoryMarshal.GetArrayDataReference(items), size); } diff --git a/src/tests/Regressions/coreclr/GitHub_96876/test96876.cs b/src/tests/Regressions/coreclr/GitHub_96876/test96876.cs new file mode 100644 index 00000000000000..3408854470b737 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_96876/test96876.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 System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Test96876 +{ + [Fact] + public static void TestEntryPoint() + { + Assert.True(foo(new string[1])); + Assert.False(foo(new string[1])); + + Assert.True(foo2()); + Assert.False(foo2()); + } + + // Validate that the type equality involving shared array types is handled correctly + // in shared generic code. + [MethodImpl(MethodImplOptions.NoInlining)] + static bool foo(string[] list) => typeof(T[]) == list.GetType(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool foo2() => typeof(T[]) == typeof(string[]); +} diff --git a/src/tests/Regressions/coreclr/GitHub_96876/test96876.csproj b/src/tests/Regressions/coreclr/GitHub_96876/test96876.csproj new file mode 100644 index 00000000000000..6eeaf93f848b55 --- /dev/null +++ b/src/tests/Regressions/coreclr/GitHub_96876/test96876.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + From b8d78b769f05fbc4b316ead3140e8691402f49d7 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 18 Jan 2024 15:49:59 -0300 Subject: [PATCH 103/189] Bump protocol version to check if we are in correct version that has all the things to make icordebug work. (#97161) --- src/mono/mono/component/debugger-protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/component/debugger-protocol.h b/src/mono/mono/component/debugger-protocol.h index 2379f5361ee6be..5948cef59c2c1a 100644 --- a/src/mono/mono/component/debugger-protocol.h +++ b/src/mono/mono/component/debugger-protocol.h @@ -11,7 +11,7 @@ */ #define MAJOR_VERSION 2 -#define MINOR_VERSION 65 +#define MINOR_VERSION 66 typedef enum { MDBGPROT_CMD_COMPOSITE = 100 From 72c4c57a3c8c1b267447f253fdc14f9d14d91a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Cincura=20=E2=86=B9?= Date: Thu, 18 Jan 2024 20:07:25 +0100 Subject: [PATCH 104/189] Add configuration for GDV with max type checks 3. (#97153) --- eng/pipelines/coreclr/perf-non-wasm-jobs.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/eng/pipelines/coreclr/perf-non-wasm-jobs.yml b/eng/pipelines/coreclr/perf-non-wasm-jobs.yml index d6eae98ad9f109..79dce1867bf695 100644 --- a/eng/pipelines/coreclr/perf-non-wasm-jobs.yml +++ b/eng/pipelines/coreclr/perf-non-wasm-jobs.yml @@ -263,6 +263,24 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml logicalmachine: 'perfowl' + # run coreclr perfowl microbenchmarks perf gdv3 jobs + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: coreclr + platforms: + - linux_x64 + - windows_x64 + jobParameters: + testGroup: perf + liveLibrariesBuildConfig: Release + projectFile: microbenchmarks.proj + runKind: micro + runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml + logicalmachine: 'perfowl' + experimentName: 'gdv3' + # run coreclr crossgen perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: From fae3b8842c83d2864b137e984d930697c381d10a Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Thu, 18 Jan 2024 11:33:10 -0800 Subject: [PATCH 105/189] Upgrade version of BinLogToSln (#97173) --- eng/common/templates/job/source-index-stage1.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b98202aa02d824..b0d40c1879b679 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -1,6 +1,6 @@ parameters: runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 + sourceIndexPackageVersion: 1.1.0.1-20240118.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] From 6ee4059113d2f0b05a4758fc7cf16b55c6632885 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 18 Jan 2024 14:36:18 -0500 Subject: [PATCH 106/189] [mono][llvm] Add some micro optimizations to the code handling the mrgctx argument. (#97138) * Allow the mrgctx variable to be non-volatile so llvm can allocate it to a register. * Emit the argument to mini_init_method_rgctx () in the llvm backend, so its emitted into the out-of-line bblock which is rarely executed. --- src/mono/mono/mini/method-to-ir.c | 9 +++++--- src/mono/mono/mini/mini-llvm.c | 36 ++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index eaf21803418bef..418f5bb478c09c 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -1362,7 +1362,7 @@ mono_create_rgctx_var (MonoCompile *cfg) if (!cfg->rgctx_var) { cfg->rgctx_var = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL); /* force the var to be stack allocated */ - if (!cfg->llvm_only) + if (!COMPILE_LLVM (cfg)) cfg->rgctx_var->flags |= MONO_INST_VOLATILE; if (cfg->verbose_level > 2) { printf ("\trgctx : "); @@ -2519,7 +2519,7 @@ emit_get_rgctx (MonoCompile *cfg, int context_used) g_assert (method->is_inflated && mono_method_get_context (method)->method_inst); */ - if (cfg->llvm_only) { + if (COMPILE_LLVM (cfg)) { mrgctx_var = mono_get_mrgctx_var (cfg); } else { /* Volatile */ @@ -6793,7 +6793,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b args [0] = mono_get_mrgctx_var (cfg); if (COMPILE_LLVM (cfg) || cfg->backend->have_init_mrgctx) { - if (cfg->compile_aot) + if (COMPILE_LLVM (cfg)) + /* OP_INIT_MRGCTX emits it itself */ + EMIT_NEW_PCONST (cfg, args [1], NULL); + else if (cfg->compile_aot) args [1] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_GSHARED_METHOD_INFO, info); else EMIT_NEW_PCONST (cfg, args [1], info); diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 86825212fa22dd..77e389a51cf9a2 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -4211,24 +4211,37 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder) } if (cfg->rgctx_var) { + LLVMValueRef rgctx_alloc = NULL; + + /* + * The rgxtx arg is received in a non-abi register. It needs to be saved to the stack so its + * accessible during EH. + */ if (!(cfg->rgctx_var->flags & MONO_INST_VOLATILE)) { /* FIXME: This could be volatile even in llvmonly mode if used inside a clause etc. */ g_assert (!ctx->addresses [cfg->rgctx_var->dreg]); - ctx->values [cfg->rgctx_var->dreg] = ctx->rgctx_arg; + if (cfg->llvm_only) { + ctx->values [cfg->rgctx_var->dreg] = ctx->rgctx_arg; + } else { + rgctx_alloc = mono_llvm_build_alloca (builder, ThisType (), const_int32 (1), 0, ""); + /* This volatile store will keep the alloca alive */ + emit_store (builder, ctx->rgctx_arg, rgctx_alloc, TRUE); + /* + * Load it into a normal llvm value instead of using rgctx_arg, the non-abi register could cause codegen + * problems otherwise. + */ + ctx->values [cfg->rgctx_var->dreg] = emit_load (builder, ThisType (), convert (ctx, rgctx_alloc, ThisType ()), "mrgctx", TRUE); + } } else { - LLVMValueRef rgctx_alloc, store; - - /* - * We handle the rgctx arg similarly to the this pointer. - */ + /* The rgctx var is volatile so it already has an address, and there is no need to set ctx->values */ g_assert (ctx->addresses [cfg->rgctx_var->dreg]); rgctx_alloc = ctx->addresses [cfg->rgctx_var->dreg]->value; /* This volatile store will keep the alloca alive */ - store = emit_store (builder, convert (ctx, ctx->rgctx_arg, IntPtrType ()), rgctx_alloc, TRUE); - (void)store; /* unused */ + emit_store (builder, convert (ctx, ctx->rgctx_arg, IntPtrType ()), rgctx_alloc, TRUE); + } + if (rgctx_alloc) set_metadata_flag (rgctx_alloc, "mono.this"); - } } #ifdef TARGET_WASM @@ -7741,8 +7754,11 @@ MONO_RESTORE_WARNING if (!icall_sig) icall_sig = LLVMFunctionType2 (LLVMVoidType (), IntPtrType (), IntPtrType (), FALSE); + /* Emit this here instead of receiving it as an argument so the load ends up in the out-of-line bb */ + LLVMValueRef info = get_aotconst (ctx, MONO_PATCH_INFO_GSHARED_METHOD_INFO, cfg->gshared_info, pointer_type (IntPtrType ())); + args [0] = convert (ctx, lhs, IntPtrType ()); - args [1] = convert (ctx, rhs, IntPtrType ()); + args [1] = convert (ctx, info, IntPtrType ()); callee = get_callee (ctx, icall_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mini_init_method_rgctx)); LLVMBuildCall2 (builder, icall_sig, callee, args, 2, ""); From 05fabacb4fc3cf34faf21819b6fcc9892d2484c0 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 18 Jan 2024 14:36:40 -0500 Subject: [PATCH 107/189] [mono] Revert https://github.com/dotnet/runtime/pull/96946. (#97165) It causes perf regressions: https://github.com/dotnet/perf-autofiling-issues/issues/27367. --- src/mono/mono/utils/options-def.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mono/mono/utils/options-def.h b/src/mono/mono/utils/options-def.h index 7ca9413bcae3cd..8f3ec6d47c4e9f 100644 --- a/src/mono/mono/utils/options-def.h +++ b/src/mono/mono/utils/options-def.h @@ -161,7 +161,11 @@ DEFINE_INT(jiterpreter_table_size, "jiterpreter-table-size", 6 * 1024, "Size of DEFINE_INT(jiterpreter_aot_table_size, "jiterpreter-aot-table-size", 3 * 1024, "Size of the jiterpreter AOT trampoline function tables") #endif // HOST_BROWSER +#if defined(TARGET_WASM) || defined(TARGET_IOS) || defined(TARGET_TVOS) || defined (TARGET_MACCAT) DEFINE_BOOL_READONLY(experimental_gshared_mrgctx, "experimental-gshared-mrgctx", TRUE, "Use a mrgctx for all gshared methods") +#else +DEFINE_BOOL(experimental_gshared_mrgctx, "experimental-gshared-mrgctx", FALSE, "Use a mrgctx for all gshared methods") +#endif #if defined(TARGET_WASI) DEFINE_BOOL_READONLY(llvm_emulate_unwind, "emulate-unwind", TRUE, "") From 3ca0c5a26292b8eaed92935840a199663c375636 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 18 Jan 2024 21:21:00 +0100 Subject: [PATCH 108/189] Collect SPMI collections as part of Fuzzlyn pipeline (#97154) This will include SPMI collections recorded over the reduction process in the zip artifact produced by the Fuzzlyn runs. --- src/coreclr/scripts/fuzzlyn_run.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/scripts/fuzzlyn_run.py b/src/coreclr/scripts/fuzzlyn_run.py index 0fe88f79a7f975..002d3548583f86 100644 --- a/src/coreclr/scripts/fuzzlyn_run.py +++ b/src/coreclr/scripts/fuzzlyn_run.py @@ -135,10 +135,13 @@ def run(self): if reduce_this: print("Reducing {}".format(ex['Seed'])) output_path = path.join(self.examples_dir, str(ex["Seed"]) + ".cs") + spmi_collections_path = path.join(self.examples_dir, str(ex["Seed"]) + "_spmi") + os.mkdir(spmi_collections_path) cmd = [self.fuzzlyn_path, "--host", self.host_path, "--reduce", "--seed", str(ex['Seed']), + "--collect-spmi-to", spmi_collections_path, "--output", output_path] run_command(cmd) if path.exists(output_path): From 2f63046c28f2f57f2c8666845f1c1de8696b1626 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 18 Jan 2024 21:54:27 +0100 Subject: [PATCH 109/189] JIT: Validate loop invariant base case as part of IV analysis (#97122) Move the validation of the base case of the IV analysis into `FlowGraphNaturalLoop::AnalyzeIteration`. Previously the validation for the base case was left up to the users: - Loop cloning tried to accomplish this by checking whether loop inversion had run, but this is not sufficient (#96623) as nothing guarantees the loop is dominated by the inverted test. Loop cloning also skipped the check entirely for GDV, leading to #95315. - Unrolling completely neglected to check the condition leading to #96591. Furthermore, unrolling made implicit assumptions that any `BBJ_COND` init block was an inverted test and downright removed the condition without any checks. This also led to another issue #97040 where unrolling could uncover new natural loops that had not been canonicalized. This change makes `FlowGraphNaturalLoop::AnalyzeIteration` try to prove that the loop invariant it returns is true in the base case as well as being maintained by the loop. If it cannot prove this then it fails. This fixes all the issues, but sadly uncovers that we were doing a lot of cloning in OSR methods without actually proving legality. We no longer clone in these cases, but we can look into what to do about them separately. Fix #95315 Fix #96591 Fix #96623 Fix #97040 --- src/coreclr/jit/compiler.h | 25 ++- src/coreclr/jit/flowgraph.cpp | 176 +++++++++++++++--- src/coreclr/jit/loopcloning.cpp | 5 + src/coreclr/jit/optimizer.cpp | 137 ++++---------- .../JitBlue/Runtime_95315/Runtime_95315.cs | 56 ++++++ .../Runtime_95315/Runtime_95315.csproj | 15 ++ .../JitBlue/Runtime_96591/Runtime_96591.cs | 29 +++ .../Runtime_96591/Runtime_96591.csproj | 10 + .../JitBlue/Runtime_96591/Runtime_96591_2.cs | 29 +++ .../Runtime_96591/Runtime_96591_2.csproj | 10 + .../JitBlue/Runtime_96623/Runtime_96623.cs | 62 ++++++ .../Runtime_96623/Runtime_96623.csproj | 8 + 12 files changed, 436 insertions(+), 126 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.csproj create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.csproj create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.csproj create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.csproj diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 80e0dc70809c78..cdff1b9fbfe0ae 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2006,14 +2006,16 @@ struct NaturalLoopIterInfo // The local that is the induction variable. unsigned IterVar = BAD_VAR_NUM; +#ifdef DEBUG + // Tree that initializes induction varaible outside the loop. + // Only valid if HasConstInit is true. + GenTree* InitTree = nullptr; +#endif + // Constant value that the induction variable is initialized with, outside // the loop. Only valid if HasConstInit is true. int ConstInitValue = 0; - // Block outside the loop that initializes the induction variable. Only - // valid if HasConstInit is true. - BasicBlock* InitBlock = nullptr; - // Tree that has the loop test for the induction variable. GenTree* TestTree = nullptr; @@ -2023,6 +2025,9 @@ struct NaturalLoopIterInfo // Tree that mutates the induction variable. GenTree* IterTree = nullptr; + // Is the loop exited when TestTree is true? + bool ExitedOnTrue : 1; + // Whether or not we found an initialization of the induction variable. bool HasConstInit : 1; @@ -2042,7 +2047,8 @@ struct NaturalLoopIterInfo bool HasArrayLengthLimit : 1; NaturalLoopIterInfo() - : HasConstInit(false) + : ExitedOnTrue(false) + , HasConstInit(false) , HasConstLimit(false) , HasSimdLimit(false) , HasInvariantLocalLimit(false) @@ -2053,7 +2059,6 @@ struct NaturalLoopIterInfo int IterConst(); genTreeOps IterOper(); var_types IterOperType(); - bool IsReversed(); genTreeOps TestOper(); bool IsIncreasingLoop(); bool IsDecreasingLoop(); @@ -2062,6 +2067,9 @@ struct NaturalLoopIterInfo int ConstLimit(); unsigned VarLimit(); bool ArrLenLimit(Compiler* comp, ArrIndex* index); + +private: + bool IsReversed(); }; // Represents a natural loop in the flow graph. Natural loops are characterized @@ -2136,7 +2144,9 @@ class FlowGraphNaturalLoop void MatchInit(NaturalLoopIterInfo* info, BasicBlock* initBlock, GenTree* init); bool MatchLimit(NaturalLoopIterInfo* info, GenTree* test); - + bool CheckLoopConditionBaseCase(BasicBlock* initBlock, NaturalLoopIterInfo* info); + template + static bool EvaluateRelop(T op1, T op2, genTreeOps oper); public: BasicBlock* GetHeader() const { @@ -7201,7 +7211,6 @@ class Compiler var_types iterType, genTreeOps testOper, bool unsignedTest, - bool dupCond, unsigned* iterCount); protected: diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 7a12698b8481af..7966292d7fff30 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4933,15 +4933,31 @@ GenTreeLclVarCommon* FlowGraphNaturalLoop::FindDef(unsigned lclNum) // otherwise false. // // Remarks: -// The function guarantees that at all definitions of the iteration local, -// the loop condition is reestablished before iteration continues. In other -// words, if this function returns true the loop invariant is guaranteed to -// be upheld in all blocks in the loop (but see below for establishing the -// base case). -// -// Currently we do not validate that there is a zero-trip test ensuring the -// condition is true, so it is up to the user to validate that. This is -// normally done via GTF_RELOP_ZTT set by loop inversion. +// On a true return, the function guarantees that the loop invariant is true +// and maintained at all points within the loop, except possibly right after +// the update of the iterator variable (NaturalLoopIterInfo::IterTree). The +// function guarantees that the test (NaturalLoopIterInfo::TestTree) occurs +// immediately after the update, so no IR in the loop is executed without the +// loop invariant being true, except for the test. +// +// The loop invariant is defined as the expression obtained by +// [info->IterVar] [info->TestOper()] [info->Limit()]. Note that +// [info->TestTree()] may not be of this form; it could for instance have the +// iterator variable as the second operand. However, +// [NaturalLoopIterInfo::TestOper()] will automatically normalize the test +// oper so that the invariant is equivalent to the returned form that has the +// iteration variable as op1 and the limit as op2. +// +// The limit can be further decomposed via NaturalLoopIterInfo::ConstLimit, +// ::VarLimit and ::ArrLenLimit. +// +// As an example, if info->IterVar == V02, info->TestOper() == GT_LT and +// info->ConstLimit() == 10, then the function guarantees that the value of +// the local V02 is less than 10 everywhere within the loop (except possibly +// at the test). +// +// In some cases we also know the initial value on entry to the loop; see +// ::HasConstInit and ::ConstInitValue. // bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info) { @@ -4972,8 +4988,15 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info) return false; } - info->TestBlock = cond; - info->IterVar = comp->optIsLoopIncrTree(info->IterTree); + assert(ContainsBlock(cond->GetTrueTarget()) != ContainsBlock(cond->GetFalseTarget())); + + info->TestBlock = cond; + info->IterVar = comp->optIsLoopIncrTree(info->IterTree); + info->ExitedOnTrue = !ContainsBlock(cond->GetTrueTarget()); + + // TODO-CQ: Currently, since we only look at the lexically bottom most block all loops have + // ExitedOnTrue == false. Once we recognize more structures this can be true. + assert(!info->ExitedOnTrue); assert(info->IterVar != BAD_VAR_NUM); LclVarDsc* const iterVarDsc = comp->lvaGetDesc(info->IterVar); @@ -5027,13 +5050,20 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info) return false; } + if (!CheckLoopConditionBaseCase(initBlock, info)) + { + JITDUMP(" Loop condition is not always true\n"); + return false; + } + #ifdef DEBUG if (comp->verbose) { printf(" IterVar = V%02u\n", info->IterVar); if (info->HasConstInit) - printf(" Const init with value %d in " FMT_BB "\n", info->ConstInitValue, info->InitBlock->bbNum); + printf(" Const init with value %d (at [%06u])\n", info->ConstInitValue, + Compiler::dspTreeID(info->InitTree)); printf(" Test is [%06u] (", Compiler::dspTreeID(info->TestTree)); if (info->HasConstLimit) @@ -5075,7 +5105,7 @@ void FlowGraphNaturalLoop::MatchInit(NaturalLoopIterInfo* info, BasicBlock* init info->HasConstInit = true; info->ConstInitValue = (int)initValue->AsIntCon()->IconValue(); - info->InitBlock = initBlock; + INDEBUG(info->InitTree = init); } //------------------------------------------------------------------------ @@ -5118,12 +5148,12 @@ bool FlowGraphNaturalLoop::MatchLimit(NaturalLoopIterInfo* info, GenTree* test) GenTree* limitOp; // Make sure op1 or op2 is the iterVar. - if (opr1->gtOper == GT_LCL_VAR && opr1->AsLclVarCommon()->GetLclNum() == info->IterVar) + if (opr1->OperIsScalarLocal() && (opr1->AsLclVarCommon()->GetLclNum() == info->IterVar)) { iterOp = opr1; limitOp = opr2; } - else if (opr2->gtOper == GT_LCL_VAR && opr2->AsLclVarCommon()->GetLclNum() == info->IterVar) + else if (opr2->OperIsScalarLocal() && (opr2->AsLclVarCommon()->GetLclNum() == info->IterVar)) { iterOp = opr2; limitOp = opr1; @@ -5138,11 +5168,8 @@ bool FlowGraphNaturalLoop::MatchLimit(NaturalLoopIterInfo* info, GenTree* test) return false; } - // Mark the iterator node. - iterOp->gtFlags |= GTF_VAR_ITERATOR; - // Check what type of limit we have - constant, variable or arr-len. - if (limitOp->gtOper == GT_CNS_INT) + if (limitOp->IsCnsIntOrI()) { info->HasConstLimit = true; if ((limitOp->gtFlags & GTF_ICON_SIMD_COUNT) != 0) @@ -5212,6 +5239,105 @@ bool FlowGraphNaturalLoop::MatchLimit(NaturalLoopIterInfo* info, GenTree* test) return true; } +//------------------------------------------------------------------------ +// EvaluateRelop: Evaluate a relational operator with constant arguments. +// +// Parameters: +// op1 - First operand +// op2 - Second operand +// oper - Operator +// +// Returns: +// Result. +// +template +bool FlowGraphNaturalLoop::EvaluateRelop(T op1, T op2, genTreeOps oper) +{ + switch (oper) + { + case GT_EQ: + return op1 == op2; + + case GT_NE: + return op1 != op2; + + case GT_LT: + return op1 < op2; + + case GT_LE: + return op1 <= op2; + + case GT_GT: + return op1 > op2; + + case GT_GE: + return op1 >= op2; + + default: + unreached(); + } +} + +//------------------------------------------------------------------------ +// CheckLoopConditionBaseCase: Verify that the loop condition is true when the +// loop is entered (or validated immediately on entry). +// +// Returns: +// True if we could prove that the condition is true at all interesting +// points in the loop. +// +// Remarks: +// Currently handles the following cases: +// * The condition being trivially true in the first iteration (e.g. for (int i = 0; i < 3; i++)) +// * The condition is checked before entry (often due to loop inversion) +// +bool FlowGraphNaturalLoop::CheckLoopConditionBaseCase(BasicBlock* initBlock, NaturalLoopIterInfo* info) +{ + // TODO: A common loop idiom is to enter the loop at the test, with the + // unique in-loop predecessor of the header block being the increment. We + // currently do not handle these patterns in `optExtractInitTestIncr`. + // Instead we depend on loop inversion to put them into an + // if (x) { do { ... } while (x) } + // form. Once we handle the pattern in `optExtractInitTestIncr` we can + // handle it here by checking for whether the test is the header and first + // thing in the header. + + // Is it trivially true? + if (info->HasConstInit && info->HasConstLimit) + { + int initVal = info->ConstInitValue; + int limitVal = info->ConstLimit(); + + assert(genActualType(info->TestTree->gtGetOp1()) == TYP_INT); + + bool isTriviallyTrue = info->TestTree->IsUnsigned() + ? EvaluateRelop((uint32_t)initVal, (uint32_t)limitVal, info->TestOper()) + : EvaluateRelop(initVal, limitVal, info->TestOper()); + + if (isTriviallyTrue) + { + JITDUMP(" Condition is trivially true on entry (%d %s%s %d)\n", initVal, + info->TestTree->IsUnsigned() ? "(uns)" : "", GenTree::OpName(info->TestOper()), limitVal); + return true; + } + } + + // Do we have a zero-trip test? + if (initBlock->KindIs(BBJ_COND)) + { + GenTree* enteringJTrue = initBlock->lastStmt()->GetRootNode(); + assert(enteringJTrue->OperIs(GT_JTRUE)); + if (enteringJTrue->gtGetOp1()->OperIsCompare() && ((enteringJTrue->gtGetOp1()->gtFlags & GTF_RELOP_ZTT) != 0)) + { + JITDUMP(" Condition is established before entry at [%06u]\n", + Compiler::dspTreeID(enteringJTrue->gtGetOp1())); + return true; + } + } + + return false; +} + //------------------------------------------------------------------------ // GetLexicallyTopMostBlock: Get the lexically top-most block contained within // the loop. @@ -5340,7 +5466,7 @@ var_types NaturalLoopIterInfo::IterOperType() // bool NaturalLoopIterInfo::IsReversed() { - return TestTree->gtGetOp2()->OperIs(GT_LCL_VAR) && ((TestTree->gtGetOp2()->gtFlags & GTF_VAR_ITERATOR) != 0); + return TestTree->gtGetOp2()->OperIsScalarLocal() && (TestTree->gtGetOp2()->AsLclVar()->GetLclNum() == IterVar); } //------------------------------------------------------------------------ @@ -5353,7 +5479,15 @@ bool NaturalLoopIterInfo::IsReversed() genTreeOps NaturalLoopIterInfo::TestOper() { genTreeOps op = TestTree->OperGet(); - return IsReversed() ? GenTree::SwapRelop(op) : op; + if (IsReversed()) + { + op = GenTree::SwapRelop(op); + } + if (ExitedOnTrue) + { + op = GenTree::ReverseRelop(op); + } + return op; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 108bdf4e5181ab..fc9703feafcad2 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -1819,6 +1819,9 @@ bool Compiler::optIsLoopClonable(FlowGraphNaturalLoop* loop, LoopCloneContext* c // Is the entry block a handler or filter start? If so, then if we cloned, we could create a jump // into the middle of a handler (to go to the cloned copy.) Reject. + // TODO: This seems like it can be deleted. If the header is the beginning + // of a handler then the loop should be fully contained within the handler, + // and the cloned loop will also be in the handler. if (bbIsHandlerBeg(loop->GetHeader())) { JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Header block is a handler start.\n", loop->GetIndex()); @@ -1873,6 +1876,8 @@ bool Compiler::optIsLoopClonable(FlowGraphNaturalLoop* loop, LoopCloneContext* c return false; } + // TODO-Quirk: Can be removed, loop invariant is validated by + // `FlowGraphNaturalLoop::AnalyzeIteration`. if (!iterInfo->TestTree->OperIsCompare() || ((iterInfo->TestTree->gtFlags & GTF_RELOP_ZTT) == 0)) { JITDUMP("Loop cloning: rejecting loop " FMT_LP diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index a0f40efa975251..bc4f133f55a010 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -2445,19 +2445,18 @@ bool Compiler::optIterSmallUnderflow(int iterAtExit, var_types decrType) } //----------------------------------------------------------------------------- -// optComputeLoopRep: Helper for loop unrolling. Computes the number of repetitions -// in a constant loop. +// optComputeLoopRep: Helper for loop unrolling. Computes the number of times +// the test block of a loop is executed. // // Arguments: -// constInit - loop constant initial value -// constLimit - loop constant limit -// iterInc - loop iteration increment -// iterOper - loop iteration increment operator (ADD, SUB, etc.) -// iterOperType - iteration operator type -// testOper - type of loop test (i.e. GT_LE, GT_GE, etc.) -// unsTest - true if test is unsigned -// dupCond - true if the loop head contains a test which skips this loop -// iterCount - *iterCount is set to the iteration count, if the function returns `true` +// constInit - loop constant initial value +// constLimit - loop constant limit +// iterInc - loop iteration increment +// iterOper - loop iteration increment operator (ADD, SUB, etc.) +// iterOperType - iteration operator type +// testOper - type of loop test (i.e. GT_LE, GT_GE, etc.) +// unsTest - true if test is unsigned +// iterCount - *iterCount is set to the iteration count, if the function returns `true` // // Returns: // true if the loop has a constant repetition count, false if that cannot be proven @@ -2469,7 +2468,6 @@ bool Compiler::optComputeLoopRep(int constInit, var_types iterOperType, genTreeOps testOper, bool unsTest, - bool dupCond, unsigned* iterCount) { noway_assert(genActualType(iterOperType) == TYP_INT); @@ -2535,23 +2533,9 @@ bool Compiler::optComputeLoopRep(int constInit, return false; } - // Set iterSign to +1 for positive iterInc and -1 for negative iterInc. - iterSign = (iterInc > 0) ? +1 : -1; - - // Initialize loopCount to zero. + iterSign = (iterInc > 0) ? +1 : -1; loopCount = 0; - // If dupCond is true then the loop initialization block contains a test which skips - // this loop, if the constInit does not pass the loop test. - // Such a loop can execute zero times. - // If dupCond is false then we have a true do-while loop where we - // always execute the loop once before performing the loop test. - if (!dupCond) - { - loopCount += 1; - constInitX += iterInc; - } - // bail if count is based on wrap-around math if (iterInc > 0) { @@ -2595,7 +2579,6 @@ bool Compiler::optComputeLoopRep(int constInit, } else { - noway_assert(iterInc < 0); // Stepping by -1, i.e. Mod with 1 is always zero. if (iterInc != -1) { @@ -3041,41 +3024,31 @@ bool Compiler::optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR) // - increment operation type (i.e. ADD, SUB, etc...) // - loop test type (i.e. GT_GE, GT_LT, etc...) - BasicBlock* initBlock = iterInfo.InitBlock; - int lbeg = iterInfo.ConstInitValue; - int llim = iterInfo.ConstLimit(); - genTreeOps testOper = iterInfo.TestOper(); - unsigned lvar = iterInfo.IterVar; - int iterInc = iterInfo.IterConst(); - genTreeOps iterOper = iterInfo.IterOper(); - var_types iterOperType = iterInfo.IterOperType(); - bool unsTest = (iterInfo.TestTree->gtFlags & GTF_UNSIGNED) != 0; + int lbeg = iterInfo.ConstInitValue; + int llim = iterInfo.ConstLimit(); + genTreeOps testOper = iterInfo.TestOper(); + unsigned lvar = iterInfo.IterVar; + int iterInc = iterInfo.IterConst(); + genTreeOps iterOper = iterInfo.IterOper(); + var_types iterOperType = iterInfo.IterOperType(); + bool unsTest = (iterInfo.TestTree->gtFlags & GTF_UNSIGNED) != 0; assert(!lvaGetDesc(lvar)->IsAddressExposed()); assert(!lvaGetDesc(lvar)->lvIsStructField); - // Locate/initialize the increment/test statements. - Statement* initStmt = initBlock->lastStmt(); - noway_assert((initStmt != nullptr) && (initStmt->GetNextStmt() == nullptr)); - - bool dupCond = false; - if (initStmt->GetRootNode()->OperIs(GT_JTRUE)) - { - // Must be a duplicated loop condition. - - dupCond = true; - initStmt = initStmt->GetPrevStmt(); - noway_assert(initStmt != nullptr); - } + JITDUMP("Analyzing candidate for loop unrolling:\n"); + DBEXEC(verbose, FlowGraphNaturalLoop::Dump(loop)); // Find the number of iterations - the function returns false if not a constant number. unsigned totalIter; - if (!optComputeLoopRep(lbeg, llim, iterInc, iterOper, iterOperType, testOper, unsTest, dupCond, &totalIter)) + if (!optComputeLoopRep(lbeg, llim, iterInc, iterOper, iterOperType, testOper, unsTest, &totalIter)) { JITDUMP("Failed to unroll loop " FMT_LP ": not a constant iteration count\n", loop->GetIndex()); return false; } + JITDUMP("Computed loop repetition count (number of test block executions) to be %u\n", totalIter); + // Forget it if there are too many repetitions or not a constant loop. if (totalIter > iterLimit) @@ -3124,21 +3097,14 @@ bool Compiler::optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR) } incr = incr->AsLclVar()->Data(); - GenTree* init = initStmt->GetRootNode(); - // Make sure everything looks ok. assert((iterInfo.TestBlock != nullptr) && iterInfo.TestBlock->KindIs(BBJ_COND)); // clang-format off - if (!init->OperIs(GT_STORE_LCL_VAR) || - (init->AsLclVar()->GetLclNum() != lvar) || - !init->AsLclVar()->Data()->IsCnsIntOrI() || - (init->AsLclVar()->Data()->AsIntCon()->gtIconVal != lbeg) || - - !((incr->gtOper == GT_ADD) || (incr->gtOper == GT_SUB)) || - (incr->AsOp()->gtOp1->gtOper != GT_LCL_VAR) || + if (!incr->OperIs(GT_ADD, GT_SUB) || + !incr->AsOp()->gtOp1->OperIs(GT_LCL_VAR) || (incr->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum() != lvar) || - (incr->AsOp()->gtOp2->gtOper != GT_CNS_INT) || + !incr->AsOp()->gtOp2->OperIs(GT_CNS_INT) || (incr->AsOp()->gtOp2->AsIntCon()->gtIconVal != iterInc) || (iterInfo.TestBlock->lastStmt()->GetRootNode()->gtGetOp1() != iterInfo.TestTree)) @@ -3420,10 +3386,15 @@ bool Compiler::optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR) } } - // If we get here, we successfully cloned all the blocks in the unrolled loop. - // Note we may not have done any cloning at all, if the loop iteration count was zero. - // Now redirect the last iteration to the real exit of the loop (or - // the entry to the exit if we unrolled 0 iterations). + // If we get here, we successfully cloned all the blocks in the + // unrolled loop. Note we may not have done any cloning at all if + // the loop iteration count was computed to be zero. Such loops are + // guaranteed to be unreachable since if the repetition count is + // zero the loop invariant is false on the first iteration, yet + // FlowGraphNaturalLoop::AnalyzeIteration only returns true if the + // loop invariant is true on every iteration. That means we have a + // guarding check before we enter the loop that will always be + // false. if (prevTestBlock != nullptr) { assert(prevTestBlock->KindIs(BBJ_ALWAYS)); @@ -3440,49 +3411,21 @@ bool Compiler::optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR) BasicBlock* entering = entryEdge->getSourceBlock(); assert(!entering->KindIs(BBJ_COND)); // Ensured by canonicalization optRedirectBlock(entering, &blockMap, Compiler::RedirectBlockOption::UpdatePredLists); - JITDUMP("Redirecting original entry " FMT_BB " -> " FMT_BB " to " FMT_BB " -> " FMT_BB "\n", entering->bbNum, loop->GetHeader()->bbNum, entering->bbNum, exit->bbNum); } } - // The old loop body is unreachable now. - - // Control will fall through from the initBlock to its successor, which is either - // the preheader HEAD (if it exists), or the now empty TOP (if totalIter == 0), - // or the first cloned top. - // - // If the initBlock is a BBJ_COND drop the condition (and make initBlock a BBJ_ALWAYS block). - // - // TODO: Isn't this missing validity checks? This seems dangerous. - // - if (initBlock->KindIs(BBJ_COND)) - { - assert(dupCond); - Statement* initBlockBranchStmt = initBlock->lastStmt(); - noway_assert(initBlockBranchStmt->GetRootNode()->OperIs(GT_JTRUE)); - fgRemoveStmt(initBlock, initBlockBranchStmt); - fgRemoveRefPred(initBlock->GetTrueTarget(), initBlock); - initBlock->SetKindAndTarget(BBJ_ALWAYS, initBlock->GetFalseTarget()); - - // TODO-NoFallThrough: If bbFalseTarget can diverge from bbNext, it may not make sense to set - // BBF_NONE_QUIRK - initBlock->SetFlags(BBF_NONE_QUIRK); - } - else - { - // the loop must execute - assert(!dupCond); - assert(totalIter > 0); - noway_assert(initBlock->KindIs(BBJ_ALWAYS)); - } + // The old loop body is unreachable now, but we will remove those + // blocks after we finish unrolling. + CLANG_FORMAT_COMMENT_ANCHOR; #ifdef DEBUG if (verbose) { printf("Whole unrolled loop:\n"); - gtDispTree(initStmt->GetRootNode()); + gtDispTree(iterInfo.InitTree); printf("\n"); fgDumpTrees(bottom->Next(), insertAfter); } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.cs b/src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.cs new file mode 100644 index 00000000000000..3f15e179afb9fc --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.cs @@ -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. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Xunit; + +public class Runtime_95315 +{ + [Fact] + public static void TestEntryPoint() + { + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 200; j++) + { + Assert.Throws(() => Bar(new int[10], new C())); + } + + Thread.Sleep(100); + } + + Assert.Throws(() => Bar(new int[1], new C())); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Bar(int[] arr, I iface) + { + if (arr == null) + { + return; + } + + iface.Foo(); + + int i = 10000000; + do + { + Console.WriteLine(arr[i]); + i++; + } while (i < arr.Length); + } +} + +internal interface I +{ + void Foo(); +} + +internal class C : I +{ + public void Foo() + { + } +} \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.csproj new file mode 100644 index 00000000000000..9ecac50f2ea8fc --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_95315/Runtime_95315.csproj @@ -0,0 +1,15 @@ + + + True + None + true + + true + + + + + + + + \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.cs b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.cs new file mode 100644 index 00000000000000..807d236bf3b0e7 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_96591 +{ + [Fact] + public static int TestEntryPoint() + { + return 100 - Foo(0); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Foo(int y) + { + int x = 0; + if (y != 0) + { + do + { + x++; + } + while (x < 4); + } + return x; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.csproj new file mode 100644 index 00000000000000..6c2e6c34ba4034 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591.csproj @@ -0,0 +1,10 @@ + + + True + None + true + + + + + \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.cs b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.cs new file mode 100644 index 00000000000000..372270a283d843 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_96591_2 +{ + [Fact] + public static int TestEntryPoint() + { + return 99 + Foo(1); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Foo(int y) + { + int x = 0; + if (y != 0) + { + do + { + x++; + } + while (x < 0); + } + return x; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.csproj new file mode 100644 index 00000000000000..6c2e6c34ba4034 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_96591/Runtime_96591_2.csproj @@ -0,0 +1,10 @@ + + + True + None + true + + + + + \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.cs b/src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.cs new file mode 100644 index 00000000000000..c3c7f052d4cc61 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_96623 +{ + [Fact] + public static void TestEntryPoint() + { + Assert.Throws(() => Foo(new int[15])); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Foo(int[] arr) + { + int limit = GetLimit(); + int sum = 0; + int i = 0; + int j = 0; + + int x; + + if (Environment.TickCount < 0) + x = 42; + else + x = 42; + + if (x == 42) + { + i = int.MaxValue; + goto TestInner; + } + + Console.WriteLine("Unreachable"); + goto TestOuter; + +OuterStart:; + Console.WriteLine("Outer"); + goto TestInner; + +InnerStart:; + sum += arr[i]; + +TestInner:; + j++; + if (j < 30) + goto InnerStart; + +TestOuter:; + i++; + if (i < limit) + goto OuterStart; + + return sum; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int GetLimit() => 10; +} \ No newline at end of file diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.csproj new file mode 100644 index 00000000000000..de6d5e08882e86 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_96623/Runtime_96623.csproj @@ -0,0 +1,8 @@ + + + True + + + + + From 6fe8ff295f01a05eb149cc3b1656d1f2cb84c100 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 18 Jan 2024 16:31:35 -0800 Subject: [PATCH 110/189] Initialize nextRefPosition (#97169) --- src/coreclr/jit/lsraarm64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 991513cfa33cbd..ea3bc9d7fb37e0 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -37,7 +37,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX RefPosition* LinearScan::getNextConsecutiveRefPosition(RefPosition* refPosition) { assert(compiler->info.compNeedsConsecutiveRegisters); - RefPosition* nextRefPosition; + RefPosition* nextRefPosition = nullptr; assert(refPosition->needsConsecutive); nextConsecutiveRefPositionMap->Lookup(refPosition, &nextRefPosition); assert((nextRefPosition == nullptr) || nextRefPosition->needsConsecutive); From d0306911c97b1df2743060248d747c063bd5553b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 18 Jan 2024 17:54:39 -0800 Subject: [PATCH 111/189] Make multiple calls to FinalRelease safe (#97059) * Don't double-release a COM object * Add a test * Make the release thread-safe * Add using * Update src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs Co-authored-by: Aaron Robinson * Update src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs * Update src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs * Add name parameter * Fix analyzer error * SuppressFinalize earlier * Add tests for the exceptional scenarios --------- Co-authored-by: Aaron Robinson --- .../InteropServices/Marshalling/ComObject.cs | 9 +- .../Marshalling/ComObjectTests.cs | 124 ++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshalling/ComObjectTests.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs index fab2920fec7cf5..216b38d7de253f 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/Marshalling/ComObject.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.Versioning; +using System.Threading; namespace System.Runtime.InteropServices.Marshalling { @@ -22,6 +23,9 @@ public sealed unsafe class ComObject : IDynamicInterfaceCastable, IUnmanagedVirt private readonly object? _runtimeCallableWrapper; + // This is an int so we can use the Interlocked APIs to update it. + private volatile int _released; + /// /// Initialize ComObject instance. /// @@ -77,8 +81,9 @@ internal ComObject(IIUnknownInterfaceDetailsStrategy interfaceDetailsStrategy, I /// public void FinalRelease() { - if (UniqueInstance) + if (UniqueInstance && Interlocked.CompareExchange(ref _released, 1, 0) == 0) { + GC.SuppressFinalize(this); CacheStrategy.Clear(IUnknownStrategy); IUnknownStrategy.Release(_instancePointer); } @@ -110,6 +115,8 @@ bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfac private bool LookUpVTableInfo(RuntimeTypeHandle handle, out IIUnknownCacheStrategy.TableInfo result, out int qiHResult) { + ObjectDisposedException.ThrowIf(_released != 0, this); + qiHResult = 0; if (!CacheStrategy.TryGetTableInfo(handle, out result)) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshalling/ComObjectTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshalling/ComObjectTests.cs new file mode 100644 index 00000000000000..2d693f6033ffec --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshalling/ComObjectTests.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices.Tests +{ + public class ComObjectTests + { + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + public void ComObject_FinalRelease() + { + var (ptr, weakReference) = CreateComObject(); + var comWrappers = new StrategyBasedComWrappers(); + var comObject = (Marshalling.ComObject)comWrappers.GetOrCreateObjectForComInterface((nint)ptr, CreateObjectFlags.UniqueInstance); + Marshal.Release((nint)ptr); + comObject.FinalRelease(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + // The underlying object should be collected as FinalRelease should have released all references to the managed object wrapper. + Assert.False(weakReference.IsAlive); + // FinalRelease should not crash if called a second time (it should be a no-op). + comObject.FinalRelease(); + + } + + [Fact] + public void ComObject_FinalRelease_ThrowingCacheStrategy() + { + var (ptr, _) = CreateComObject(); + var comWrappers = new ThrowingClearComWrappers(); + var comObject = (Marshalling.ComObject)comWrappers.GetOrCreateObjectForComInterface((nint)ptr, CreateObjectFlags.UniqueInstance); + Marshal.Release((nint)ptr); + Assert.Throws(() => comObject.FinalRelease()); + GC.Collect(); + // The finalizer should not throw as the object should have suppressed finalization. + GC.WaitForPendingFinalizers(); + + // FinalRelease should not throw an exception if called a second time (it should be a no-op). + comObject.FinalRelease(); + + // We'll manually release again to ensure that we don't leak. + Marshal.Release((nint)ptr); + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.False(weakReference.IsAlive); + } + + sealed class ThrowingClearComWrappers : StrategyBasedComWrappers + { + protected override IIUnknownCacheStrategy CreateCacheStrategy() + => new ThrowingClearCacheStrategy(base.CreateCacheStrategy()); + + + private sealed class ThrowingClearCacheStrategy(IIUnknownCacheStrategy inner) : IIUnknownCacheStrategy + { + IIUnknownCacheStrategy.TableInfo IIUnknownCacheStrategy.ConstructTableInfo(RuntimeTypeHandle handle, IIUnknownDerivedDetails details, void* ptr) + => inner.ConstructTableInfo(handle, details, ptr); + + bool IIUnknownCacheStrategy.TryGetTableInfo(RuntimeTypeHandle handle, out IIUnknownCacheStrategy.TableInfo info) + => inner.TryGetTableInfo(handle, out info); + + bool IIUnknownCacheStrategy.TrySetTableInfo(RuntimeTypeHandle handle, IIUnknownCacheStrategy.TableInfo info) + => inner.TrySetTableInfo(handle, info); + + void IIUnknownCacheStrategy.Clear(IIUnknownStrategy unknownStrategy) + { + throw new InvalidOperationException(); + } + } + } + + [Fact] + public void ComObject_FinalRelease_ThrowingIUnknownStrategy() + { + var (ptr, _) = CreateComObject(); + var comWrappers = new ThrowingReleaseComWrappers(); + var comObject = (Marshalling.ComObject)comWrappers.GetOrCreateObjectForComInterface((nint)ptr, CreateObjectFlags.UniqueInstance); + Marshal.Release((nint)ptr); + Assert.Throws(() => comObject.FinalRelease()); + GC.Collect(); + // The finalizer should not throw as the object should have suppressed finalization. + GC.WaitForPendingFinalizers(); + + // FinalRelease should not throw an exception if called a second time (it should be a no-op). + comObject.FinalRelease(); + + // We'll manually release again to ensure that we don't leak. + Marshal.Release((nint)ptr); + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.False(weakReference.IsAlive); + } + + sealed class ThrowingReleaseComWrappers : StrategyBasedComWrappers + { + protected override IIUnknownStrategy GetOrCreateIUnknownStrategy() + => new ThrowingReleaseStrategy(); + + + private sealed class ThrowingReleaseStrategy : IIUnknownStrategy + { + void* IIUnknownStrategy.CreateInstancePointer(void* unknown) => unknown; + + unsafe int IIUnknownStrategy.QueryInterface(void* thisPtr, in Guid handle, out void* ppObj) + => unchecked((int)0x80004002); // E_NOINTERFACE + + unsafe int IIUnknownStrategy.Release(void* thisPtr) + => throw new InvalidOperationException(); + } + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + private static (nint ptr, WeakReference objRef) CreateComObject() + { + var managedObject = new object(); + var managedObjectWrapper = new Common.ComWrappersImpl(); + IntPtr ptr = cw.GetOrCreateComInterfaceForObject(o, CreateComInterfaceFlags.None); + var comWrappers = new StrategyBasedComWrappers(); + var ptr = Common.ComObject.Create(); + var comObject = (Marshalling.ComObject)comWrappers.GetOrCreateObjectForComInterface((nint)ptr, CreateObjectFlags.UniqueInstance); + return ((nint)ptr, new WeakReference(comObject)); + } + } +} From 7a34dfc22e0e2ca601d6d27ed6d7b4d1a2de87f0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 18 Jan 2024 17:54:56 -0800 Subject: [PATCH 112/189] Use ParallelTheory attribute to run cases in parallel (#97111) This will speed up test runs by allowing the many cases in these tests to run in parallel instead of sequentially. --- .../CompileFails.cs | 17 +++++++++-------- .../Compiles.cs | 5 +++-- .../CompileFails.cs | 5 +++-- .../Compiles.cs | 15 ++++++++------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs index 198e3370c04cd0..a4fad790817cb1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; +using Microsoft.DotNet.XUnitExtensions.Attributes; using Microsoft.Interop; using Microsoft.Interop.UnitTests; using Xunit; @@ -40,7 +41,7 @@ public static IEnumerable ComInterfaceGeneratorSnippetsToCompile() } }; } - [Theory] + [ParallelTheory] [MemberData(nameof(ComInterfaceGeneratorSnippetsToCompile))] public async Task ValidateComInterfaceGeneratorSnippets(string id, string source, DiagnosticResult[] expectedDiagnostics) { @@ -334,7 +335,7 @@ public static IEnumerable InvalidManagedToUnmanagedCodeSnippetsToCompi yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateful.ByValueOutParameter }; } - [Theory] + [ParallelTheory] [MemberData(nameof(InvalidUnmanagedToManagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)] public async Task ValidateInvalidUnmanagedToManagedCodeSnippets(string id, string source, DiagnosticResult[] expectedDiagnostics) { @@ -348,7 +349,7 @@ public async Task ValidateInvalidUnmanagedToManagedCodeSnippets(string id, strin await test.RunAsync(); } - [Theory] + [ParallelTheory] [MemberData(nameof(InvalidManagedToUnmanagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)] public async Task ValidateInvalidManagedToUnmanagedCodeSnippets(string id, string source) { @@ -360,7 +361,7 @@ public async Task ValidateInvalidManagedToUnmanagedCodeSnippets(string id, strin await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic); } - [Theory] + [ParallelTheory] [MemberData(nameof(StringMarshallingCodeSnippets), GeneratorKind.ComInterfaceGenerator)] public async Task ValidateStringMarshallingDiagnostics(string id, string source, DiagnosticResult[] expectedDiagnostics) { @@ -511,7 +512,7 @@ public static unsafe partial class Test { } } - [Theory] + [ParallelTheory] [MemberData(nameof(StringMarshallingCustomTypeVisibilities))] public async Task VerifyStringMarshallingCustomTypeWithLessVisibilityThanInterfaceWarns(string id, string source, DiagnosticResult[] diagnostics) { @@ -519,7 +520,7 @@ public async Task VerifyStringMarshallingCustomTypeWithLessVisibilityThanInterfa await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, diagnostics); } - [Theory] + [ParallelTheory] [MemberData(nameof(InterfaceVisibilities))] public async Task VerifyInterfaceWithLessVisibilityThanInterfaceWarns(string id, string source, DiagnosticResult[] diagnostics) { @@ -771,7 +772,7 @@ string Source( } } - [Theory] + [ParallelTheory] [MemberData(nameof(CountParameterIsOutSnippets))] public async Task ValidateSizeParameterRefKindDiagnostics(string ID, string source, params DiagnosticResult[] diagnostics) { @@ -888,7 +889,7 @@ partial interface I } } - [Theory] + [ParallelTheory] [MemberData(nameof(IntAndEnumReturnTypeSnippets))] public async Task ValidateReturnTypeInfoDiagnostics(string id, string source, DiagnosticResult[] diagnostics) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs index bf04b97ffcded6..421e989f870bad 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions.Attributes; using Microsoft.Interop.UnitTests; using Xunit; using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; @@ -319,7 +320,7 @@ public static IEnumerable CustomCollectionsManagedToUnmanaged(Generato yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementNativeToManagedOnlyReturnValue }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompile), GeneratorKind.VTableIndexStubGenerator)] [MemberData(nameof(ManagedToUnmanagedCodeSnippetsToCompile), GeneratorKind.VTableIndexStubGenerator)] [MemberData(nameof(UnmanagedToManagedCodeSnippetsToCompile), GeneratorKind.VTableIndexStubGenerator)] @@ -347,7 +348,7 @@ public static IEnumerable ManagedToUnmanagedComInterfaceSnippetsToComp yield return new[] { ID(), codeSnippets.MarshalAsParameterAndModifiers("object", System.Runtime.InteropServices.UnmanagedType.Struct) }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)] [MemberData(nameof(CustomCollections), GeneratorKind.ComInterfaceGenerator)] [MemberData(nameof(ManagedToUnmanagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGeneratorComObjectWrapper)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index 2d278dc6237de2..676cd56e2e6a67 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; +using Microsoft.DotNet.XUnitExtensions.Attributes; using Microsoft.Interop; using Microsoft.Interop.UnitTests; using Xunit; @@ -784,7 +785,7 @@ public static IEnumerable CodeSnippetsToCompile() } }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompile))] public async Task ValidateSnippets(string id, string source, DiagnosticResult[] diagnostics) { @@ -803,7 +804,7 @@ public static IEnumerable CodeSnippetsToCompile_InvalidCode() yield return new[] { ID(), CodeSnippets.IncorrectAttributeFieldType }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompile_InvalidCode))] public async Task ValidateSnippets_InvalidCodeGracefulFailure(string id, string source) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs index 226c3661b9dc09..03f678773a895a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; +using Microsoft.DotNet.XUnitExtensions.Attributes; using Microsoft.Interop.UnitTests; using Xunit; using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; @@ -438,7 +439,7 @@ public static IEnumerable CustomCollections() yield return new[] { ID(), CodeSnippets.CollectionsOfCollectionsStress }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompile))] [MemberData(nameof(CustomCollections))] public async Task ValidateSnippets(string id, string source) @@ -459,7 +460,7 @@ public static IEnumerable CodeSnippetsToCompileWithPreprocessorSymbols yield return new object[] { ID(), CodeSnippets.PreprocessorIfAfterAttributeAroundFunctionAdditionalFunctionAfter("Foo"), new string[] { "Foo" } }; yield return new object[] { ID(), CodeSnippets.PreprocessorIfAfterAttributeAroundFunctionAdditionalFunctionAfter("Foo"), Array.Empty() }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompileWithPreprocessorSymbols))] public async Task ValidateSnippetsWithPreprocessorDefinitions(string id, string source, IEnumerable preprocessorSymbols) { @@ -528,7 +529,7 @@ public static IEnumerable CodeSnippetsToValidateFallbackForwarder() } } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToValidateFallbackForwarder))] [OuterLoop("Uses the network for downlevel ref packs")] public async Task ValidateSnippetsFallbackForwarder(string id, string source, TestTargetFramework targetFramework, bool expectFallbackForwarder) @@ -574,7 +575,7 @@ public static IEnumerable FullyBlittableSnippetsToCompile() yield return new[] { ID(), CodeSnippets.BasicParameterByValue("int") }; } - [Theory] + [ParallelTheory] [MemberData(nameof(FullyBlittableSnippetsToCompile))] public async Task ValidateSnippetsWithBlittableAutoForwarding(string id, string source) { @@ -614,7 +615,7 @@ public static IEnumerable SnippetsWithBlittableTypesButNonBlittableDat yield return new[] { ID(), CodeSnippets.SetLastErrorTrue() }; } - [Theory] + [ParallelTheory] [MemberData(nameof(SnippetsWithBlittableTypesButNonBlittableDataToCompile))] public async Task ValidateSnippetsWithBlittableTypesButNonBlittableMetadataDoNotAutoForward(string id, string source) { @@ -687,7 +688,7 @@ public static IEnumerable CodeSnippetsToCompileMultipleSources() yield return new object[] { ID(), new[] { CodeSnippets.BasicParameterByValue("int[]", CodeSnippets.DisableRuntimeMarshalling), CodeSnippets.BasicParameterWithByRefModifier("ref", "int") } }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToCompileMultipleSources))] public async Task ValidateSnippetsWithMultipleSources(string id, string[] sources) { @@ -715,7 +716,7 @@ public class Basic { } yield return new object[] { ID(), source, TestTargetFramework.Net }; } - [Theory] + [ParallelTheory] [MemberData(nameof(CodeSnippetsToVerifyNoTreesProduced))] public async Task ValidateNoGeneratedOuptutForNoImport(string id, string source, TestTargetFramework framework) { From 2939fde09e594070205f8bda036485c7b398241c Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 19 Jan 2024 03:10:53 +0100 Subject: [PATCH 113/189] Add ContainsAny{Except} path to SearchValues (#96924) --- .../src/System/MemoryExtensions.cs | 22 +- .../SearchValues/AnyByteSearchValues.cs | 24 +- .../SearchValues/AsciiByteSearchValues.cs | 24 +- .../SearchValues/AsciiCharSearchValues.cs | 24 +- .../SearchValues/IndexOfAnyAsciiSearcher.cs | 278 ++++++++++++------ .../ProbabilisticWithAsciiCharSearchValues.cs | 12 +- .../src/System/SearchValues/SearchValues.T.cs | 3 + .../Strings/Helpers/AhoCorasick.cs | 4 +- 8 files changed, 271 insertions(+), 120 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 4d8ff44fd202e2..bffab304afb0c8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -492,8 +492,15 @@ public static bool ContainsAny(this ReadOnlySpan span, ReadOnlySpan val /// The span to search. /// The set of values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsAny(this ReadOnlySpan span, SearchValues values) where T : IEquatable? => - IndexOfAny(span, values) >= 0; + public static bool ContainsAny(this ReadOnlySpan span, SearchValues values) where T : IEquatable? + { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + return values.ContainsAny(span); + } /// /// Searches for any occurrence of any of the specified substring and returns true if found. If not found, returns false. @@ -569,8 +576,15 @@ public static bool ContainsAnyExcept(this ReadOnlySpan span, ReadOnlySpan< /// If all of the values are in , returns false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsAnyExcept(this ReadOnlySpan span, SearchValues values) where T : IEquatable? => - IndexOfAnyExcept(span, values) >= 0; + public static bool ContainsAnyExcept(this ReadOnlySpan span, SearchValues values) where T : IEquatable? + { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + return values.ContainsAnyExcept(span); + } /// /// Searches for any value in the range between and , inclusive, and returns true if found. If not found, returns false. diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs index d95d557bd89a48..7d2524371edc7a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs @@ -28,7 +28,7 @@ internal override bool ContainsCore(byte value) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -36,7 +36,7 @@ internal override int IndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -44,7 +44,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.LastIndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -52,7 +52,23 @@ internal override int LastIndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs index 3fc67d6086a198..57b755b2ce8a42 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs @@ -28,7 +28,7 @@ internal override bool ContainsCore(byte value) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -36,7 +36,7 @@ internal override int IndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -44,7 +44,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -52,7 +52,23 @@ internal override int LastIndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs index 4df9f55252f198..111a3ad313b9da 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs @@ -29,7 +29,7 @@ internal override bool ContainsCore(char value) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -37,7 +37,7 @@ internal override int IndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -45,7 +45,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -53,7 +53,23 @@ internal override int LastIndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs index 4f3ff556492ff7..bc8b3fd0c6c8f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs @@ -150,8 +150,8 @@ private static unsafe bool TryIndexOfAny(ref short searchSpace, int se state.Bitmap = Vector256.Create(state.Bitmap._lower, state.Bitmap._lower); index = (Ssse3.IsSupported || PackedSimd.IsSupported) && needleContainsZero - ? IndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state) - : IndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state); + ? IndexOfAny(ref searchSpace, searchSpaceLength, ref state) + : IndexOfAny(ref searchSpace, searchSpaceLength, ref state); return true; } } @@ -177,8 +177,8 @@ private static unsafe bool TryLastIndexOfAny(ref short searchSpace, in state.Bitmap = Vector256.Create(state.Bitmap._lower, state.Bitmap._lower); index = (Ssse3.IsSupported || PackedSimd.IsSupported) && needleContainsZero - ? LastIndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state) - : LastIndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state); + ? LastIndexOfAny(ref searchSpace, searchSpaceLength, ref state) + : LastIndexOfAny(ref searchSpace, searchSpaceLength, ref state); return true; } } @@ -187,12 +187,32 @@ private static unsafe bool TryLastIndexOfAny(ref short searchSpace, in return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int IndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + public static bool ContainsAny(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator + where TOptimizations : struct, IOptimizations => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + public static int IndexOfAny(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator + where TOptimizations : struct, IOptimizations => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + private static TResult IndexOfAnyCore(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + where TResult : struct where TNegator : struct, INegator where TOptimizations : struct, IOptimizations + where TResultMapper : struct, IResultMapper { ref short currentSearchSpace = ref searchSpace; @@ -205,13 +225,13 @@ internal static int IndexOfAnyVectorized(ref short sea char c = (char)currentSearchSpace; if (TNegator.NegateIfNeeded(state.Lookup.Contains128(c))) { - return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref currentSearchSpace) / sizeof(char)); + return TResultMapper.ScalarResult(ref searchSpace, ref currentSearchSpace); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 1); } - return -1; + return TResultMapper.NotFound; } #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false @@ -238,7 +258,7 @@ internal static int IndexOfAnyVectorized(ref short sea Vector256 result = IndexOfAnyLookup(source0, source1, bitmap256); if (result != Vector256.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256.Count); @@ -262,11 +282,11 @@ internal static int IndexOfAnyVectorized(ref short sea Vector256 result = IndexOfAnyLookup(source0, source1, bitmap256); if (result != Vector256.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } Vector128 bitmap = state.Bitmap._lower; @@ -291,7 +311,7 @@ internal static int IndexOfAnyVectorized(ref short sea Vector128 result = IndexOfAnyLookup(source0, source1, bitmap); if (result != Vector128.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128.Count); @@ -315,17 +335,17 @@ internal static int IndexOfAnyVectorized(ref short sea Vector128 result = IndexOfAnyLookup(source0, source1, bitmap); if (result != Vector128.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int LastIndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + public static int LastIndexOfAny(ref short searchSpace, int searchSpaceLength, ref AsciiState state) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations { @@ -451,11 +471,29 @@ internal static int LastIndexOfAnyVectorized(ref short return -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int IndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + public static bool ContainsAny(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + private static TResult IndexOfAnyCore(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + where TResult : struct where TNegator : struct, INegator + where TResultMapper : struct, IResultMapper { ref byte currentSearchSpace = ref searchSpace; @@ -468,13 +506,13 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea byte b = currentSearchSpace; if (TNegator.NegateIfNeeded(state.Lookup.Contains(b))) { - return (int)Unsafe.ByteOffset(ref searchSpace, ref currentSearchSpace); + return TResultMapper.ScalarResult(ref searchSpace, ref currentSearchSpace); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 1); } - return -1; + return TResultMapper.NotFound; } #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false @@ -498,7 +536,7 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector256 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap256)); if (result != Vector256.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256.Count); @@ -523,11 +561,11 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector256 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap256)); if (result != Vector256.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } Vector128 bitmap = state.Bitmap._lower; @@ -547,7 +585,7 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector128 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap)); if (result != Vector128.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128.Count); @@ -572,17 +610,17 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector128 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap)); if (result != Vector128.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) where TNegator : struct, INegator { if (searchSpaceLength < sizeof(ulong)) @@ -703,11 +741,29 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int return -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + public static bool ContainsAny(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + private static TResult IndexOfAnyCore(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + where TResult : struct where TNegator : struct, INegator + where TResultMapper : struct, IResultMapper { ref byte currentSearchSpace = ref searchSpace; @@ -720,13 +776,13 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, byte b = currentSearchSpace; if (TNegator.NegateIfNeeded(state.Lookup.Contains(b))) { - return (int)Unsafe.ByteOffset(ref searchSpace, ref currentSearchSpace); + return TResultMapper.ScalarResult(ref searchSpace, ref currentSearchSpace); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 1); } - return -1; + return TResultMapper.NotFound; } #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false @@ -751,7 +807,7 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector256 result = IndexOfAnyLookup(source, bitmap256_0, bitmap256_1); if (result != Vector256.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256.Count); @@ -776,11 +832,11 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector256 result = IndexOfAnyLookup(source, bitmap256_0, bitmap256_1); if (result != Vector256.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } Vector128 bitmap0 = state.Bitmap0._lower; @@ -803,7 +859,7 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector128 result = IndexOfAnyLookup(source, bitmap0, bitmap1); if (result != Vector128.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128.Count); @@ -828,17 +884,17 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector128 result = IndexOfAnyLookup(source, bitmap0, bitmap1); if (result != Vector128.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int LastIndexOfAnyVectorizedAnyByte(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) where TNegator : struct, INegator { if (!IsVectorizationSupported || searchSpaceLength < sizeof(ulong)) @@ -1081,30 +1137,6 @@ private static Vector256 IndexOfAnyLookup(Vector256 source return TNegator.NegateIfNeeded(result); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector128 result) - where TNegator : struct, INegator - { - uint mask = TNegator.ExtractMask(result); - int offsetInVector = BitOperations.TrailingZeroCount(mask); - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) - where TNegator : struct, INegator - { - uint mask = TNegator.ExtractMask(result); - int offsetInVector = BitOperations.TrailingZeroCount(mask); - if (offsetInVector >= Vector128.Count) - { - // We matched within the second vector - current0 = ref current1; - offsetInVector -= Vector128.Count; - } - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator @@ -1129,44 +1161,6 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp return offsetInVector - Vector128.Count + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref secondVector) / (nuint)sizeof(T)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(Avx2))] - private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector256 result) - where TNegator : struct, INegator - { - if (typeof(T) == typeof(short)) - { - result = PackedSpanHelpers.FixUpPackedVector256Result(result); - } - - uint mask = TNegator.ExtractMask(result); - - int offsetInVector = BitOperations.TrailingZeroCount(mask); - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(Avx2))] - private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) - where TNegator : struct, INegator - { - if (typeof(T) == typeof(short)) - { - result = PackedSpanHelpers.FixUpPackedVector256Result(result); - } - - uint mask = TNegator.ExtractMask(result); - - int offsetInVector = BitOperations.TrailingZeroCount(mask); - if (offsetInVector >= Vector256.Count) - { - // We matched within the second vector - current0 = ref current1; - offsetInVector -= Vector256.Count; - } - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Avx2))] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector256 result) @@ -1294,5 +1288,97 @@ public static Vector256 PackSources(Vector256 lower, Vector256 + where TResult : struct + { + static abstract TResult NotFound { get; } + + static abstract TResult ScalarResult(ref T searchSpace, ref T current); + static abstract TResult FirstIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator; + static abstract TResult FirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator; + static abstract TResult FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) where TNegator : struct, INegator; + static abstract TResult FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator; + } + + private readonly struct ContainsAnyResultMapper : IResultMapper + { + public static bool NotFound => false; + + public static bool ScalarResult(ref T searchSpace, ref T current) => true; + public static bool FirstIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator => true; + public static bool FirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator => true; + public static bool FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) where TNegator : struct, INegator => true; + public static bool FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator => true; + } + + private readonly unsafe struct IndexOfAnyResultMapper : IResultMapper + { + public static int NotFound => -1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ScalarResult(ref T searchSpace, ref T current) + { + return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FirstIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator + { + uint mask = TNegator.ExtractMask(result); + int offsetInVector = BitOperations.TrailingZeroCount(mask); + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Avx2))] + public static int FirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator + { + if (typeof(T) == typeof(short)) + { + result = PackedSpanHelpers.FixUpPackedVector256Result(result); + } + + uint mask = TNegator.ExtractMask(result); + + int offsetInVector = BitOperations.TrailingZeroCount(mask); + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) where TNegator : struct, INegator + { + uint mask = TNegator.ExtractMask(result); + int offsetInVector = BitOperations.TrailingZeroCount(mask); + if (offsetInVector >= Vector128.Count) + { + // We matched within the second vector + current0 = ref current1; + offsetInVector -= Vector128.Count; + } + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Avx2))] + public static int FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator + { + if (typeof(T) == typeof(short)) + { + result = PackedSpanHelpers.FixUpPackedVector256Result(result); + } + + uint mask = TNegator.ExtractMask(result); + + int offsetInVector = BitOperations.TrailingZeroCount(mask); + if (offsetInVector >= Vector256.Count) + { + // We matched within the second vector + current0 = ref current1; + offsetInVector -= Vector256.Count; + } + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs index 7ef2b3c23a2036..b9b9227aa3ff6c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs @@ -57,7 +57,7 @@ internal override int IndexOfAny(ReadOnlySpan span) { Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0."); - offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -67,7 +67,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle."); - offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -107,7 +107,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128.Count && char.IsAscii(span[0])) { // Do a regular IndexOfAnyExcept for the ASCII characters. The search will stop if we encounter a non-ASCII char. - offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _asciiState); @@ -157,7 +157,7 @@ internal override int LastIndexOfAny(ReadOnlySpan span) { Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0."); - offset = IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -167,7 +167,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle."); - offset = IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -197,7 +197,7 @@ internal override int LastIndexOfAnyExcept(ReadOnlySpan span) if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128.Count && char.IsAscii(span[^1])) { // Do a regular LastIndexOfAnyExcept for the ASCII characters. The search will stop if we encounter a non-ASCII char. - int offset = IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + int offset = IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _asciiState); diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs index 62736734ed1bdb..65a54082f93b9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs @@ -39,6 +39,9 @@ private protected SearchValues() { } internal virtual int LastIndexOfAny(ReadOnlySpan span) => throw new UnreachableException(); internal virtual int LastIndexOfAnyExcept(ReadOnlySpan span) => throw new UnreachableException(); + internal virtual bool ContainsAny(ReadOnlySpan span) => IndexOfAny(span) >= 0; + internal virtual bool ContainsAnyExcept(ReadOnlySpan span) => IndexOfAnyExcept(span) >= 0; + // This is only implemented and used by SearchValues. internal virtual int IndexOfAnyMultiString(ReadOnlySpan span) => throw new UnreachableException(); diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs index bcd7693d80ecc6..ebc94616ae642c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs @@ -96,7 +96,7 @@ private readonly int IndexOfAnyCore(ReadOnly // If '\0' is one of the starting chars and we're running on Ssse3 hardware, this may return false-positives. // False-positives here are okay, we'll just rule them out below. While we could flow the Ssse3AndWasmHandleZeroInNeedle // generic through, we expect such values to be rare enough that introducing more code is not worth it. - int offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + int offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), i)), remainingLength, ref Unsafe.AsRef(in _startingAsciiChars)); @@ -205,7 +205,7 @@ private readonly int IndexOfAnyCaseInsensitiveUnicode(ReadOnly if (remainingLength >= Vector128.Count) { - int offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + int offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), i)), remainingLength, ref Unsafe.AsRef(in _startingAsciiChars)); From 59a38f18b777155f01c66afde35f2c0a48850cb0 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 18 Jan 2024 19:09:19 -0800 Subject: [PATCH 114/189] Ensure syncblock is cleared with GC_ALLOC_ZEROING_OPTIONAL (#97174) Fixes #96790 --- src/coreclr/vm/gchelpers.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index 5bffc9a00ad4d4..ecbf8f09ce293d 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -434,7 +434,8 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS // dummy object. // If the GC gives us a 8 byte aligned address, we use it for the array and place the dummy // object after the array, otherwise we put the dummy object first, shifting the base of - // the array to an 8 byte aligned address. + // the array to an 8 byte aligned address. Also, we need to make sure that the syncblock of the + // second object is zeroed. GC won't take care of zeroing it out with GC_ALLOC_ZEROING_OPTIONAL. // // Note: on 64 bit platforms, the GC always returns 8 byte aligned addresses, and we don't // execute this code because DATA_ALIGNMENT < sizeof(double) is false. @@ -447,14 +448,24 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS orArray = (ArrayBase*)Alloc(totalSize + MIN_OBJECT_SIZE, flags); Object* orDummyObject; - if ((size_t)orArray % sizeof(double)) + if (((size_t)orArray % sizeof(double)) != 0) { orDummyObject = orArray; orArray = (ArrayBase*)((size_t)orArray + MIN_OBJECT_SIZE); + if (flags & GC_ALLOC_ZEROING_OPTIONAL) + { + // clean the syncblock of the aligned array. + *(((void**)orArray)-1) = 0; + } } else { orDummyObject = (Object*)((size_t)orArray + totalSize); + if (flags & GC_ALLOC_ZEROING_OPTIONAL) + { + // clean the syncblock of the dummy object. + *(((void**)orDummyObject)-1) = 0; + } } _ASSERTE(((size_t)orArray % sizeof(double)) == 0); orDummyObject->SetMethodTable(g_pObjectClass); From c53d22162ba46c2024340e23f4ab42ed04025bee Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 18 Jan 2024 20:37:47 -0800 Subject: [PATCH 115/189] Adding vectorized implementations of Exp to Vector64/128/256/512 (#97114) * Adding vectorized implementations of Exp to Vector64/128/256/512 * Accelerate TensorPrimitives.Exp for double * Ensure the right allowedVariance is used for the vectorized exp tests * Ensure V128/256/512 defers to the next smaller vector size by operating on the lower/upper halves * Ensure the right allowedVariance amounts are used for the vectorized Exp(float) tests * Ensure we call Exp and that the methods are properly inlined * Skip the Exp test for Vector128/256/512 on Mono due to https://github.com/dotnet/runtime/issues/97176 --- .../netcore/TensorPrimitives.netcore.cs | 375 ++++++++++++++++-- .../System/Runtime/Intrinsics/Vector128.cs | 38 ++ .../System/Runtime/Intrinsics/Vector256.cs | 38 ++ .../System/Runtime/Intrinsics/Vector512.cs | 38 ++ .../src/System/Runtime/Intrinsics/Vector64.cs | 50 +++ .../System/Runtime/Intrinsics/VectorMath.cs | 322 ++++++++++++++- .../ref/System.Runtime.Intrinsics.cs | 8 + .../tests/Vectors/Vector128Tests.cs | 18 + .../tests/Vectors/Vector256Tests.cs | 18 + .../tests/Vectors/Vector512Tests.cs | 18 + .../tests/Vectors/Vector64Tests.cs | 16 + .../tests/Vectors/VectorTestMemberData.cs | 80 ++++ 12 files changed, 971 insertions(+), 48 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs index 9c41234cef3f92..41560fbb8b284f 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs @@ -10999,6 +10999,310 @@ public static Vector512 Invoke(Vector512 x) /// T.Exp(x) internal readonly struct ExpOperator : IUnaryOperator where T : IExponentialFunctions + { + public static bool Vectorizable => (typeof(T) == typeof(double)) + || (typeof(T) == typeof(float)); + + public static T Invoke(T x) => T.Exp(x); + + public static Vector128 Invoke(Vector128 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Exp(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Exp(x.AsSingle()).As(); + } +#else + if (typeof(T) == typeof(double)) + { + return ExpOperatorDouble.Invoke(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return ExpOperatorSingle.Invoke(x.AsSingle()).As(); + } +#endif + } + + public static Vector256 Invoke(Vector256 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Exp(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Exp(x.AsSingle()).As(); + } +#else + if (typeof(T) == typeof(double)) + { + return ExpOperatorDouble.Invoke(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return ExpOperatorSingle.Invoke(x.AsSingle()).As(); + } +#endif + } + + public static Vector512 Invoke(Vector512 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Exp(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Exp(x.AsSingle()).As(); + } +#else + if (typeof(T) == typeof(double)) + { + return ExpOperatorDouble.Invoke(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return ExpOperatorSingle.Invoke(x.AsSingle()).As(); + } +#endif + } + } + +#if !NET9_0_OR_GREATER + /// double.Exp(x) + internal readonly struct ExpOperatorDouble : IUnaryOperator + { + // This code is based on `vrd2_exp` from amd/aocl-libm-ose + // Copyright (C) 2019-2020 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Implementation Notes + // ---------------------- + // 1. Argument Reduction: + // e^x = 2^(x/ln2) = 2^(x*(64/ln(2))/64) --- (1) + // + // Choose 'n' and 'f', such that + // x * 64/ln2 = n + f --- (2) | n is integer + // | |f| <= 0.5 + // Choose 'm' and 'j' such that, + // n = (64 * m) + j --- (3) + // + // From (1), (2) and (3), + // e^x = 2^((64*m + j + f)/64) + // = (2^m) * (2^(j/64)) * 2^(f/64) + // = (2^m) * (2^(j/64)) * e^(f*(ln(2)/64)) + // + // 2. Table Lookup + // Values of (2^(j/64)) are precomputed, j = 0, 1, 2, 3 ... 63 + // + // 3. Polynomial Evaluation + // From (2), + // f = x*(64/ln(2)) - n + // Let, + // r = f*(ln(2)/64) = x - n*(ln(2)/64) + // + // 4. Reconstruction + // Thus, + // e^x = (2^m) * (2^(j/64)) * e^r + + private const ulong V_ARG_MAX = 0x40862000_00000000; + private const ulong V_DP64_BIAS = 1023; + + private const double V_EXPF_MIN = -709.782712893384; + private const double V_EXPF_MAX = +709.782712893384; + + private const double V_EXPF_HUGE = 6755399441055744; + private const double V_TBL_LN2 = 1.4426950408889634; + + private const double V_LN2_HEAD = +0.693359375; + private const double V_LN2_TAIL = -0.00021219444005469057; + + private const double C3 = 0.5000000000000018; + private const double C4 = 0.1666666666666617; + private const double C5 = 0.04166666666649277; + private const double C6 = 0.008333333333559272; + private const double C7 = 0.001388888895122404; + private const double C8 = 0.00019841269432677495; + private const double C9 = 2.4801486521374483E-05; + private const double C10 = 2.7557622532543023E-06; + private const double C11 = 2.7632293298250954E-07; + private const double C12 = 2.499430431958571E-08; + + public static bool Vectorizable => true; + + public static double Invoke(double x) => double.Exp(x); + + public static Vector128 Invoke(Vector128 x) + { + // x * (64.0 / ln(2)) + Vector128 z = x * Vector128.Create(V_TBL_LN2); + + Vector128 dn = z + Vector128.Create(V_EXPF_HUGE); + + // n = (int)z + Vector128 n = dn.AsUInt64(); + + // dn = (double)n + dn -= Vector128.Create(V_EXPF_HUGE); + + // r = x - (dn * (ln(2) / 64)) + // where ln(2) / 64 is split into Head and Tail values + Vector128 r = x - (dn * Vector128.Create(V_LN2_HEAD)) - (dn * Vector128.Create(V_LN2_TAIL)); + + Vector128 r2 = r * r; + Vector128 r4 = r2 * r2; + Vector128 r8 = r4 * r4; + + // Compute polynomial + Vector128 poly = ((Vector128.Create(C12) * r + Vector128.Create(C11)) * r2 + + Vector128.Create(C10) * r + Vector128.Create(C9)) * r8 + + ((Vector128.Create(C8) * r + Vector128.Create(C7)) * r2 + + (Vector128.Create(C6) * r + Vector128.Create(C5))) * r4 + + ((Vector128.Create(C4) * r + Vector128.Create(C3)) * r2 + (r + Vector128.One)); + + // m = (n - j) / 64 + // result = polynomial * 2^m + Vector128 ret = poly * ((n + Vector128.Create(V_DP64_BIAS)) << 52).AsDouble(); + + // Check if -709 < vx < 709 + if (Vector128.GreaterThanAny(Vector128.Abs(x).AsUInt64(), Vector128.Create(V_ARG_MAX))) + { + // (x > V_EXPF_MAX) ? double.PositiveInfinity : x + Vector128 infinityMask = Vector128.GreaterThan(x, Vector128.Create(V_EXPF_MAX)); + + ret = Vector128.ConditionalSelect( + infinityMask, + Vector128.Create(double.PositiveInfinity), + ret + ); + + // (x < V_EXPF_MIN) ? 0 : x + ret = Vector128.AndNot(ret, Vector128.LessThan(x, Vector128.Create(V_EXPF_MIN))); + } + + return ret; + } + + public static Vector256 Invoke(Vector256 x) + { + // x * (64.0 / ln(2)) + Vector256 z = x * Vector256.Create(V_TBL_LN2); + + Vector256 dn = z + Vector256.Create(V_EXPF_HUGE); + + // n = (int)z + Vector256 n = dn.AsUInt64(); + + // dn = (double)n + dn -= Vector256.Create(V_EXPF_HUGE); + + // r = x - (dn * (ln(2) / 64)) + // where ln(2) / 64 is split into Head and Tail values + Vector256 r = x - (dn * Vector256.Create(V_LN2_HEAD)) - (dn * Vector256.Create(V_LN2_TAIL)); + + Vector256 r2 = r * r; + Vector256 r4 = r2 * r2; + Vector256 r8 = r4 * r4; + + // Compute polynomial + Vector256 poly = ((Vector256.Create(C12) * r + Vector256.Create(C11)) * r2 + + Vector256.Create(C10) * r + Vector256.Create(C9)) * r8 + + ((Vector256.Create(C8) * r + Vector256.Create(C7)) * r2 + + (Vector256.Create(C6) * r + Vector256.Create(C5))) * r4 + + ((Vector256.Create(C4) * r + Vector256.Create(C3)) * r2 + (r + Vector256.One)); + + // m = (n - j) / 64 + // result = polynomial * 2^m + Vector256 ret = poly * ((n + Vector256.Create(V_DP64_BIAS)) << 52).AsDouble(); + + // Check if -709 < vx < 709 + if (Vector256.GreaterThanAny(Vector256.Abs(x).AsUInt64(), Vector256.Create(V_ARG_MAX))) + { + // (x > V_EXPF_MAX) ? double.PositiveInfinity : x + Vector256 infinityMask = Vector256.GreaterThan(x, Vector256.Create(V_EXPF_MAX)); + + ret = Vector256.ConditionalSelect( + infinityMask, + Vector256.Create(double.PositiveInfinity), + ret + ); + + // (x < V_EXPF_MIN) ? 0 : x + ret = Vector256.AndNot(ret, Vector256.LessThan(x, Vector256.Create(V_EXPF_MIN))); + } + + return ret; + } + + public static Vector512 Invoke(Vector512 x) + { + // x * (64.0 / ln(2)) + Vector512 z = x * Vector512.Create(V_TBL_LN2); + + Vector512 dn = z + Vector512.Create(V_EXPF_HUGE); + + // n = (int)z + Vector512 n = dn.AsUInt64(); + + // dn = (double)n + dn -= Vector512.Create(V_EXPF_HUGE); + + // r = x - (dn * (ln(2) / 64)) + // where ln(2) / 64 is split into Head and Tail values + Vector512 r = x - (dn * Vector512.Create(V_LN2_HEAD)) - (dn * Vector512.Create(V_LN2_TAIL)); + + Vector512 r2 = r * r; + Vector512 r4 = r2 * r2; + Vector512 r8 = r4 * r4; + + // Compute polynomial + Vector512 poly = ((Vector512.Create(C12) * r + Vector512.Create(C11)) * r2 + + Vector512.Create(C10) * r + Vector512.Create(C9)) * r8 + + ((Vector512.Create(C8) * r + Vector512.Create(C7)) * r2 + + (Vector512.Create(C6) * r + Vector512.Create(C5))) * r4 + + ((Vector512.Create(C4) * r + Vector512.Create(C3)) * r2 + (r + Vector512.One)); + + // m = (n - j) / 64 + // result = polynomial * 2^m + Vector512 ret = poly * ((n + Vector512.Create(V_DP64_BIAS)) << 52).AsDouble(); + + // Check if -709 < vx < 709 + if (Vector512.GreaterThanAny(Vector512.Abs(x).AsUInt64(), Vector512.Create(V_ARG_MAX))) + { + // (x > V_EXPF_MAX) ? double.PositiveInfinity : x + Vector512 infinityMask = Vector512.GreaterThan(x, Vector512.Create(V_EXPF_MAX)); + + ret = Vector512.ConditionalSelect( + infinityMask, + Vector512.Create(double.PositiveInfinity), + ret + ); + + // (x < V_EXPF_MIN) ? 0 : x + ret = Vector512.AndNot(ret, Vector512.LessThan(x, Vector512.Create(V_EXPF_MIN))); + } + + return ret; + } + } + + /// float.Exp(x) + internal readonly struct ExpOperatorSingle : IUnaryOperator { // This code is based on `vrs4_expf` from amd/aocl-libm-ose // Copyright (C) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. @@ -11030,10 +11334,9 @@ public static Vector512 Invoke(Vector512 x) // e^x = (2^N) * (2^r) private const uint V_ARG_MAX = 0x42AE0000; - private const uint V_MASK = 0x7FFFFFFF; private const float V_EXPF_MIN = -103.97208f; - private const float V_EXPF_MAX = 88.72284f; + private const float V_EXPF_MAX = +88.72284f; private const double V_EXPF_HUGE = 6755399441055744; private const double V_TBL_LN2 = 1.4426950408889634; @@ -11045,15 +11348,12 @@ public static Vector512 Invoke(Vector512 x) private const double C5 = 0.009676036358193323; private const double C6 = 0.001341000536524434; - public static bool Vectorizable => typeof(T) == typeof(float); + public static bool Vectorizable => true; - public static T Invoke(T x) => T.Exp(x); + public static float Invoke(float x) => float.Exp(x); - public static Vector128 Invoke(Vector128 t) + public static Vector128 Invoke(Vector128 x) { - Debug.Assert(typeof(T) == typeof(float)); - Vector128 x = t.AsSingle(); - // Convert x to double precision (Vector128 xl, Vector128 xu) = Vector128.Widen(x); @@ -11068,11 +11368,11 @@ public static Vector128 Invoke(Vector128 t) Vector128 dnl = zl + v_expf_huge; Vector128 dnu = zu + v_expf_huge; - // n = int (z) + // n = (int)z Vector128 nl = dnl.AsUInt64(); Vector128 nu = dnu.AsUInt64(); - // dn = double(n) + // dn = (double)n dnl -= v_expf_huge; dnu -= v_expf_huge; @@ -11103,14 +11403,14 @@ public static Vector128 Invoke(Vector128 t) + ((c6 * ru + c5) * ru4 + (c2 * ru + c1)); - // result = (float)[poly + (n << 52)] + // result = (float)(poly + (n << 52)) Vector128 ret = Vector128.Narrow( - (polyl.AsUInt64() + Vector128.ShiftLeft(nl, 52)).AsDouble(), - (polyu.AsUInt64() + Vector128.ShiftLeft(nu, 52)).AsDouble() + (polyl.AsUInt64() + (nl << 52)).AsDouble(), + (polyu.AsUInt64() + (nu << 52)).AsDouble() ); // Check if -103 < |x| < 88 - if (Vector128.GreaterThanAny(x.AsUInt32() & Vector128.Create(V_MASK), Vector128.Create(V_ARG_MAX))) + if (Vector128.GreaterThanAny(Vector128.Abs(x).AsUInt32(), Vector128.Create(V_ARG_MAX))) { // (x > V_EXPF_MAX) ? float.PositiveInfinity : x Vector128 infinityMask = Vector128.GreaterThan(x, Vector128.Create(V_EXPF_MAX)); @@ -11125,14 +11425,11 @@ public static Vector128 Invoke(Vector128 t) ret = Vector128.AndNot(ret, Vector128.LessThan(x, Vector128.Create(V_EXPF_MIN))); } - return ret.As(); + return ret; } - public static Vector256 Invoke(Vector256 t) + public static Vector256 Invoke(Vector256 x) { - Debug.Assert(typeof(T) == typeof(float)); - Vector256 x = t.AsSingle(); - // Convert x to double precision (Vector256 xl, Vector256 xu) = Vector256.Widen(x); @@ -11147,11 +11444,11 @@ public static Vector256 Invoke(Vector256 t) Vector256 dnl = zl + v_expf_huge; Vector256 dnu = zu + v_expf_huge; - // n = int (z) + // n = (int)z Vector256 nl = dnl.AsUInt64(); Vector256 nu = dnu.AsUInt64(); - // dn = double(n) + // dn = (double)n dnl -= v_expf_huge; dnu -= v_expf_huge; @@ -11182,14 +11479,14 @@ public static Vector256 Invoke(Vector256 t) + ((c6 * ru + c5) * ru4 + (c2 * ru + c1)); - // result = (float)[poly + (n << 52)] + // result = (float)(poly + (n << 52)) Vector256 ret = Vector256.Narrow( - (polyl.AsUInt64() + Vector256.ShiftLeft(nl, 52)).AsDouble(), - (polyu.AsUInt64() + Vector256.ShiftLeft(nu, 52)).AsDouble() + (polyl.AsUInt64() + (nl << 52)).AsDouble(), + (polyu.AsUInt64() + (nu << 52)).AsDouble() ); // Check if -103 < |x| < 88 - if (Vector256.GreaterThanAny(x.AsUInt32() & Vector256.Create(V_MASK), Vector256.Create(V_ARG_MAX))) + if (Vector256.GreaterThanAny(Vector256.Abs(x).AsUInt32(), Vector256.Create(V_ARG_MAX))) { // (x > V_EXPF_MAX) ? float.PositiveInfinity : x Vector256 infinityMask = Vector256.GreaterThan(x, Vector256.Create(V_EXPF_MAX)); @@ -11204,14 +11501,11 @@ public static Vector256 Invoke(Vector256 t) ret = Vector256.AndNot(ret, Vector256.LessThan(x, Vector256.Create(V_EXPF_MIN))); } - return ret.As(); + return ret; } - public static Vector512 Invoke(Vector512 t) + public static Vector512 Invoke(Vector512 x) { - Debug.Assert(typeof(T) == typeof(float)); - Vector512 x = t.AsSingle(); - // Convert x to double precision (Vector512 xl, Vector512 xu) = Vector512.Widen(x); @@ -11226,11 +11520,11 @@ public static Vector512 Invoke(Vector512 t) Vector512 dnl = zl + v_expf_huge; Vector512 dnu = zu + v_expf_huge; - // n = int (z) + // n = (int)z Vector512 nl = dnl.AsUInt64(); Vector512 nu = dnu.AsUInt64(); - // dn = double(n) + // dn = (double)n dnl -= v_expf_huge; dnu -= v_expf_huge; @@ -11261,14 +11555,14 @@ public static Vector512 Invoke(Vector512 t) + ((c6 * ru + c5) * ru4 + (c2 * ru + c1)); - // result = (float)[poly + (n << 52)] + // result = (float)(poly + (n << 52)) Vector512 ret = Vector512.Narrow( - (polyl.AsUInt64() + Vector512.ShiftLeft(nl, 52)).AsDouble(), - (polyu.AsUInt64() + Vector512.ShiftLeft(nu, 52)).AsDouble() + (polyl.AsUInt64() + (nl << 52)).AsDouble(), + (polyu.AsUInt64() + (nu << 52)).AsDouble() ); // Check if -103 < |x| < 88 - if (Vector512.GreaterThanAny(x.AsUInt32() & Vector512.Create(V_MASK), Vector512.Create(V_ARG_MAX))) + if (Vector512.GreaterThanAny(Vector512.Abs(x).AsUInt32(), Vector512.Create(V_ARG_MAX))) { // (x > V_EXPF_MAX) ? float.PositiveInfinity : x Vector512 infinityMask = Vector512.GreaterThan(x, Vector512.Create(V_EXPF_MAX)); @@ -11283,9 +11577,10 @@ public static Vector512 Invoke(Vector512 t) ret = Vector512.AndNot(ret, Vector512.LessThan(x, Vector512.Create(V_EXPF_MIN))); } - return ret.As(); + return ret; } } +#endif /// T.Cosh(x) internal readonly struct CoshOperator : IUnaryOperator @@ -12336,7 +12631,7 @@ public static Vector128 Invoke(Vector128 x) x ); - specialMask = Unsafe.BitCast, Vector128>(temp); + specialMask = temp.AsUInt64(); } // Reduce the mantissa to [+2/3, +4/3] @@ -12417,7 +12712,7 @@ public static Vector256 Invoke(Vector256 x) x ); - specialMask = Unsafe.BitCast, Vector256>(temp); + specialMask = temp.AsUInt64(); } // Reduce the mantissa to [+2/3, +4/3] @@ -12498,7 +12793,7 @@ public static Vector512 Invoke(Vector512 x) x ); - specialMask = Unsafe.BitCast, Vector512>(temp); + specialMask = temp.AsUInt64(); } // Reduce the mantissa to [+2/3, +4/3] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 0342f1b144aac6..0e54cf778d8e7e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -1426,6 +1426,40 @@ public static bool EqualsAny(Vector128 left, Vector128 right) || Vector64.EqualsAny(left._upper, right._upper); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Exp(Vector128 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpDouble, Vector128, Vector128>(vector); + } + else + { + return Create( + Vector64.Exp(vector._lower), + Vector64.Exp(vector._upper) + ); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Exp(Vector128 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpSingle, Vector128, Vector128, Vector128>(vector); + } + else + { + return Create( + Vector64.Exp(vector._lower), + Vector64.Exp(vector._upper) + ); + } + } + /// Extracts the most significant bit from each element in a vector. /// The type of the elements in the vector. /// The vector whose elements should have their most significant bit extracted. @@ -1782,6 +1816,7 @@ internal static Vector128 LoadUnsafe(ref char source, nuint elementOffse LoadUnsafe(ref Unsafe.As(ref source), elementOffset); /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Log(Vector128 vector) { if (IsHardwareAccelerated) @@ -1798,6 +1833,7 @@ public static Vector128 Log(Vector128 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Log(Vector128 vector) { if (IsHardwareAccelerated) @@ -1814,6 +1850,7 @@ public static Vector128 Log(Vector128 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Log2(Vector128 vector) { if (IsHardwareAccelerated) @@ -1830,6 +1867,7 @@ public static Vector128 Log2(Vector128 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Log2(Vector128 vector) { if (IsHardwareAccelerated) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index 9a5422e2385187..bcbd4a219a0f78 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -1402,6 +1402,40 @@ public static bool EqualsAny(Vector256 left, Vector256 right) || Vector128.EqualsAny(left._upper, right._upper); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Exp(Vector256 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpDouble, Vector256, Vector256>(vector); + } + else + { + return Create( + Vector128.Exp(vector._lower), + Vector128.Exp(vector._upper) + ); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Exp(Vector256 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpSingle, Vector256, Vector256, Vector256>(vector); + } + else + { + return Create( + Vector128.Exp(vector._lower), + Vector128.Exp(vector._upper) + ); + } + } + /// Extracts the most significant bit from each element in a vector. /// The vector whose elements should have their most significant bit extracted. /// The type of the elements in the vector. @@ -1756,6 +1790,7 @@ internal static Vector256 LoadUnsafe(ref char source, nuint elementOffse LoadUnsafe(ref Unsafe.As(ref source), elementOffset); /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Log(Vector256 vector) { if (IsHardwareAccelerated) @@ -1772,6 +1807,7 @@ public static Vector256 Log(Vector256 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Log(Vector256 vector) { if (IsHardwareAccelerated) @@ -1788,6 +1824,7 @@ public static Vector256 Log(Vector256 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Log2(Vector256 vector) { if (IsHardwareAccelerated) @@ -1804,6 +1841,7 @@ public static Vector256 Log2(Vector256 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Log2(Vector256 vector) { if (IsHardwareAccelerated) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index 42685879ebb05e..95e26012af9ba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -1453,6 +1453,40 @@ public static bool EqualsAny(Vector512 left, Vector512 right) || Vector256.EqualsAny(left._upper, right._upper); } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Exp(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpDouble, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Exp(vector._lower), + Vector256.Exp(vector._upper) + ); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Exp(Vector512 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpSingle, Vector512, Vector512, Vector512>(vector); + } + else + { + return Create( + Vector256.Exp(vector._lower), + Vector256.Exp(vector._upper) + ); + } + } + /// Extracts the most significant bit from each element in a vector. /// The vector whose elements should have their most significant bit extracted. /// The type of the elements in the vector. @@ -1807,6 +1841,7 @@ internal static Vector512 LoadUnsafe(ref char source, nuint elementOffse LoadUnsafe(ref Unsafe.As(ref source), elementOffset); /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Log(Vector512 vector) { if (IsHardwareAccelerated) @@ -1823,6 +1858,7 @@ public static Vector512 Log(Vector512 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Log(Vector512 vector) { if (IsHardwareAccelerated) @@ -1839,6 +1875,7 @@ public static Vector512 Log(Vector512 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Log2(Vector512 vector) { if (IsHardwareAccelerated) @@ -1855,6 +1892,7 @@ public static Vector512 Log2(Vector512 vector) } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Log2(Vector512 vector) { if (IsHardwareAccelerated) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index e3e4f8b3b40b02..d4789a9a0a85b3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -1156,6 +1156,52 @@ public static bool EqualsAny(Vector64 left, Vector64 right) return false; } + internal static Vector64 Exp(Vector64 vector) + where T : IExponentialFunctions + { + Unsafe.SkipInit(out Vector64 result); + + for (int index = 0; index < Vector64.Count; index++) + { + T value = T.Exp(vector.GetElement(index)); + result.SetElementUnsafe(index, value); + } + + return result; + } + + /// Computes the exp of each element in a vector. + /// The vector that will have its Exp computed. + /// A vector whose elements are the exp of the elements in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Exp(Vector64 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpDouble, Vector64, Vector64>(vector); + } + else + { + return Exp(vector); + } + } + + /// Computes the exp of each element in a vector. + /// The vector that will have its exp computed. + /// A vector whose elements are the exp of the elements in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector64 Exp(Vector64 vector) + { + if (IsHardwareAccelerated) + { + return VectorMath.ExpSingle, Vector64, Vector64, Vector64>(vector); + } + else + { + return Exp(vector); + } + } + /// Extracts the most significant bit from each element in a vector. /// The type of the elements in the vector. /// The vector whose elements should have their most significant bit extracted. @@ -1588,6 +1634,7 @@ internal static Vector64 Log(Vector64 vector) /// Computes the log of each element in a vector. /// The vector that will have its log computed. /// A vector whose elements are the log of the elements in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Log(Vector64 vector) { if (IsHardwareAccelerated) @@ -1603,6 +1650,7 @@ public static Vector64 Log(Vector64 vector) /// Computes the log of each element in a vector. /// The vector that will have its log computed. /// A vector whose elements are the log of the elements in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Log(Vector64 vector) { if (IsHardwareAccelerated) @@ -1632,6 +1680,7 @@ internal static Vector64 Log2(Vector64 vector) /// Computes the log2 of each element in a vector. /// The vector that will have its log2 computed. /// A vector whose elements are the log2 of the elements in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Log2(Vector64 vector) { if (IsHardwareAccelerated) @@ -1647,6 +1696,7 @@ public static Vector64 Log2(Vector64 vector) /// Computes the log2 of each element in a vector. /// The vector that will have its log2 computed. /// A vector whose elements are the log2 of the elements in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 Log2(Vector64 vector) { if (IsHardwareAccelerated) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs index 20e5cf83bc47a1..125c76d278a8bb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs @@ -1,12 +1,248 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.CompilerServices; namespace System.Runtime.Intrinsics { internal static class VectorMath { + public static TVectorDouble ExpDouble(TVectorDouble x) + where TVectorDouble : unmanaged, ISimdVector + where TVectorUInt64 : unmanaged, ISimdVector + { + // This code is based on `vrd2_exp` from amd/aocl-libm-ose + // Copyright (C) 2019-2020 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Implementation Notes + // ---------------------- + // 1. Argument Reduction: + // e^x = 2^(x/ln2) = 2^(x*(64/ln(2))/64) --- (1) + // + // Choose 'n' and 'f', such that + // x * 64/ln2 = n + f --- (2) | n is integer + // | |f| <= 0.5 + // Choose 'm' and 'j' such that, + // n = (64 * m) + j --- (3) + // + // From (1), (2) and (3), + // e^x = 2^((64*m + j + f)/64) + // = (2^m) * (2^(j/64)) * 2^(f/64) + // = (2^m) * (2^(j/64)) * e^(f*(ln(2)/64)) + // + // 2. Table Lookup + // Values of (2^(j/64)) are precomputed, j = 0, 1, 2, 3 ... 63 + // + // 3. Polynomial Evaluation + // From (2), + // f = x*(64/ln(2)) - n + // Let, + // r = f*(ln(2)/64) = x - n*(ln(2)/64) + // + // 4. Reconstruction + // Thus, + // e^x = (2^m) * (2^(j/64)) * e^r + + const ulong V_ARG_MAX = 0x40862000_00000000; + const ulong V_DP64_BIAS = 1023; + + const double V_EXPF_MIN = -709.782712893384; + const double V_EXPF_MAX = +709.782712893384; + + const double V_EXPF_HUGE = 6755399441055744; + const double V_TBL_LN2 = 1.4426950408889634; + + const double V_LN2_HEAD = +0.693359375; + const double V_LN2_TAIL = -0.00021219444005469057; + + const double C3 = 0.5000000000000018; + const double C4 = 0.1666666666666617; + const double C5 = 0.04166666666649277; + const double C6 = 0.008333333333559272; + const double C7 = 0.001388888895122404; + const double C8 = 0.00019841269432677495; + const double C9 = 2.4801486521374483E-05; + const double C10 = 2.7557622532543023E-06; + const double C11 = 2.7632293298250954E-07; + const double C12 = 2.499430431958571E-08; + + // x * (64.0 / ln(2)) + TVectorDouble z = x * TVectorDouble.Create(V_TBL_LN2); + + TVectorDouble dn = z + TVectorDouble.Create(V_EXPF_HUGE); + + // n = (int)z + TVectorUInt64 n = Unsafe.BitCast(dn); + + // dn = (double)n + dn -= TVectorDouble.Create(V_EXPF_HUGE); + + // r = x - (dn * (ln(2) / 64)) + // where ln(2) / 64 is split into Head and Tail values + TVectorDouble r = x - (dn * TVectorDouble.Create(V_LN2_HEAD)) - (dn * TVectorDouble.Create(V_LN2_TAIL)); + + TVectorDouble r2 = r * r; + TVectorDouble r4 = r2 * r2; + TVectorDouble r8 = r4 * r4; + + // Compute polynomial + TVectorDouble poly = ((TVectorDouble.Create(C12) * r + TVectorDouble.Create(C11)) * r2 + + TVectorDouble.Create(C10) * r + TVectorDouble.Create(C9)) * r8 + + ((TVectorDouble.Create(C8) * r + TVectorDouble.Create(C7)) * r2 + + (TVectorDouble.Create(C6) * r + TVectorDouble.Create(C5))) * r4 + + ((TVectorDouble.Create(C4) * r + TVectorDouble.Create(C3)) * r2 + (r + TVectorDouble.One)); + + // m = (n - j) / 64 + // result = polynomial * 2^m + TVectorDouble ret = poly * Unsafe.BitCast((n + TVectorUInt64.Create(V_DP64_BIAS)) << 52); + + // Check if -709 < vx < 709 + if (TVectorUInt64.GreaterThanAny(Unsafe.BitCast(TVectorDouble.Abs(x)), TVectorUInt64.Create(V_ARG_MAX))) + { + // (x > V_EXPF_MAX) ? double.PositiveInfinity : x + TVectorDouble infinityMask = TVectorDouble.GreaterThan(x, TVectorDouble.Create(V_EXPF_MAX)); + + ret = TVectorDouble.ConditionalSelect( + infinityMask, + TVectorDouble.Create(double.PositiveInfinity), + ret + ); + + // (x < V_EXPF_MIN) ? 0 : x + ret = TVectorDouble.AndNot(ret, TVectorDouble.LessThan(x, TVectorDouble.Create(V_EXPF_MIN))); + } + + return ret; + } + + public static TVectorSingle ExpSingle(TVectorSingle x) + where TVectorSingle : unmanaged, ISimdVector + where TVectorUInt32 : unmanaged, ISimdVector + where TVectorDouble : unmanaged, ISimdVector + where TVectorUInt64 : unmanaged, ISimdVector + { + // This code is based on `vrs4_expf` from amd/aocl-libm-ose + // Copyright (C) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Implementation Notes: + // 1. Argument Reduction: + // e^x = 2^(x/ln2) --- (1) + // + // Let x/ln(2) = z --- (2) + // + // Let z = n + r , where n is an integer --- (3) + // |r| <= 1/2 + // + // From (1), (2) and (3), + // e^x = 2^z + // = 2^(N+r) + // = (2^N)*(2^r) --- (4) + // + // 2. Polynomial Evaluation + // From (4), + // r = z - N + // 2^r = C1 + C2*r + C3*r^2 + C4*r^3 + C5 *r^4 + C6*r^5 + // + // 4. Reconstruction + // Thus, + // e^x = (2^N) * (2^r) + + const uint V_ARG_MAX = 0x42AE0000; + + const float V_EXPF_MIN = -103.97208f; + const float V_EXPF_MAX = +88.72284f; + + const double V_EXPF_HUGE = 6755399441055744; + const double V_TBL_LN2 = 1.4426950408889634; + + const double C1 = 1.0000000754895704; + const double C2 = 0.6931472254087585; + const double C3 = 0.2402210737432219; + const double C4 = 0.05550297297702539; + const double C5 = 0.009676036358193323; + const double C6 = 0.001341000536524434; + + // Convert x to double precision + (TVectorDouble xl, TVectorDouble xu) = Widen(x); + + // x * (64.0 / ln(2)) + TVectorDouble v_tbl_ln2 = TVectorDouble.Create(V_TBL_LN2); + + TVectorDouble zl = xl * v_tbl_ln2; + TVectorDouble zu = xu * v_tbl_ln2; + + TVectorDouble v_expf_huge = TVectorDouble.Create(V_EXPF_HUGE); + + TVectorDouble dnl = zl + v_expf_huge; + TVectorDouble dnu = zu + v_expf_huge; + + // n = (int)z + TVectorUInt64 nl = Unsafe.BitCast(dnl); + TVectorUInt64 nu = Unsafe.BitCast(dnu); + + // dn = (double)n + dnl -= v_expf_huge; + dnu -= v_expf_huge; + + // r = z - dn + TVectorDouble c1 = TVectorDouble.Create(C1); + TVectorDouble c2 = TVectorDouble.Create(C2); + TVectorDouble c3 = TVectorDouble.Create(C3); + TVectorDouble c4 = TVectorDouble.Create(C4); + TVectorDouble c5 = TVectorDouble.Create(C5); + TVectorDouble c6 = TVectorDouble.Create(C6); + + TVectorDouble rl = zl - dnl; + + TVectorDouble rl2 = rl * rl; + TVectorDouble rl4 = rl2 * rl2; + + TVectorDouble polyl = (c4 * rl + c3) * rl2 + + ((c6 * rl + c5) * rl4 + + (c2 * rl + c1)); + + + TVectorDouble ru = zu - dnu; + + TVectorDouble ru2 = ru * ru; + TVectorDouble ru4 = ru2 * ru2; + + TVectorDouble polyu = (c4 * ru + c3) * ru2 + + ((c6 * ru + c5) * ru4 + + (c2 * ru + c1)); + + // result = (float)(poly + (n << 52)) + TVectorSingle ret = Narrow( + Unsafe.BitCast(Unsafe.BitCast(polyl) + (nl << 52)), + Unsafe.BitCast(Unsafe.BitCast(polyu) + (nu << 52)) + ); + + // Check if -103 < |x| < 88 + if (TVectorUInt32.GreaterThanAny(Unsafe.BitCast(TVectorSingle.Abs(x)), TVectorUInt32.Create(V_ARG_MAX))) + { + // (x > V_EXPF_MAX) ? float.PositiveInfinity : x + TVectorSingle infinityMask = TVectorSingle.GreaterThan(x, TVectorSingle.Create(V_EXPF_MAX)); + + ret = TVectorSingle.ConditionalSelect( + infinityMask, + TVectorSingle.Create(float.PositiveInfinity), + ret + ); + + // (x < V_EXPF_MIN) ? 0 : x + ret = TVectorSingle.AndNot(ret, TVectorSingle.LessThan(x, TVectorSingle.Create(V_EXPF_MIN))); + } + + return ret; + } + public static TVectorDouble LogDouble(TVectorDouble x) where TVectorDouble : unmanaged, ISimdVector where TVectorInt64 : unmanaged, ISimdVector @@ -578,19 +814,19 @@ private static TVectorDouble ConvertToDouble(TVecto if (typeof(TVectorInt64) == typeof(Vector64)) { - return (TVectorDouble)(object)Vector64.ConvertToDouble((Vector64)(object)vector); + result = (TVectorDouble)(object)Vector64.ConvertToDouble((Vector64)(object)vector); } else if (typeof(TVectorInt64) == typeof(Vector128)) { - return (TVectorDouble)(object)Vector128.ConvertToDouble((Vector128)(object)vector); + result = (TVectorDouble)(object)Vector128.ConvertToDouble((Vector128)(object)vector); } else if (typeof(TVectorInt64) == typeof(Vector256)) { - return (TVectorDouble)(object)Vector256.ConvertToDouble((Vector256)(object)vector); + result = (TVectorDouble)(object)Vector256.ConvertToDouble((Vector256)(object)vector); } else if (typeof(TVectorInt64) == typeof(Vector512)) { - return (TVectorDouble)(object)Vector512.ConvertToDouble((Vector512)(object)vector); + result = (TVectorDouble)(object)Vector512.ConvertToDouble((Vector512)(object)vector); } else { @@ -609,19 +845,89 @@ private static TVectorSingle ConvertToSingle(TVecto if (typeof(TVectorInt32) == typeof(Vector64)) { - return (TVectorSingle)(object)Vector64.ConvertToSingle((Vector64)(object)vector); + result = (TVectorSingle)(object)Vector64.ConvertToSingle((Vector64)(object)vector); } else if (typeof(TVectorInt32) == typeof(Vector128)) { - return (TVectorSingle)(object)Vector128.ConvertToSingle((Vector128)(object)vector); + result = (TVectorSingle)(object)Vector128.ConvertToSingle((Vector128)(object)vector); } else if (typeof(TVectorInt32) == typeof(Vector256)) { - return (TVectorSingle)(object)Vector256.ConvertToSingle((Vector256)(object)vector); + result = (TVectorSingle)(object)Vector256.ConvertToSingle((Vector256)(object)vector); } else if (typeof(TVectorInt32) == typeof(Vector512)) { - return (TVectorSingle)(object)Vector512.ConvertToSingle((Vector512)(object)vector); + result = (TVectorSingle)(object)Vector512.ConvertToSingle((Vector512)(object)vector); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TVectorSingle Narrow(TVectorDouble lower, TVectorDouble upper) + where TVectorDouble : unmanaged, ISimdVector + where TVectorSingle : unmanaged, ISimdVector + { + Unsafe.SkipInit(out TVectorSingle result); + + if (typeof(TVectorDouble) == typeof(Vector64)) + { + Debug.Assert(typeof(TVectorSingle) == typeof(Vector64)); + result = (TVectorSingle)(object)Vector64.Narrow((Vector64)(object)lower, (Vector64)(object)upper); + } + else if (typeof(TVectorDouble) == typeof(Vector128)) + { + Debug.Assert(typeof(TVectorSingle) == typeof(Vector128)); + result = (TVectorSingle)(object)Vector128.Narrow((Vector128)(object)lower, (Vector128)(object)upper); + } + else if (typeof(TVectorDouble) == typeof(Vector256)) + { + Debug.Assert(typeof(TVectorSingle) == typeof(Vector256)); + result = (TVectorSingle)(object)Vector256.Narrow((Vector256)(object)lower, (Vector256)(object)upper); + } + else if (typeof(TVectorDouble) == typeof(Vector512)) + { + Debug.Assert(typeof(TVectorSingle) == typeof(Vector512)); + result = (TVectorSingle)(object)Vector512.Narrow((Vector512)(object)lower, (Vector512)(object)upper); + } + else + { + ThrowHelper.ThrowNotSupportedException(); + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static (TVectorDouble Lower, TVectorDouble Upper) Widen(TVectorSingle vector) + where TVectorSingle : unmanaged, ISimdVector + where TVectorDouble : unmanaged, ISimdVector + { + Unsafe.SkipInit(out (TVectorDouble, TVectorDouble) result); + + if (typeof(TVectorSingle) == typeof(Vector64)) + { + Debug.Assert(typeof(TVectorDouble) == typeof(Vector64)); + result = ((TVectorDouble, TVectorDouble))(object)Vector64.Widen((Vector64)(object)vector); + } + else if (typeof(TVectorSingle) == typeof(Vector128)) + { + Debug.Assert(typeof(TVectorDouble) == typeof(Vector128)); + result = ((TVectorDouble, TVectorDouble))(object)Vector128.Widen((Vector128)(object)vector); + } + else if (typeof(TVectorSingle) == typeof(Vector256)) + { + Debug.Assert(typeof(TVectorDouble) == typeof(Vector256)); + result = ((TVectorDouble, TVectorDouble))(object)Vector256.Widen((Vector256)(object)vector); + } + else if (typeof(TVectorSingle) == typeof(Vector512)) + { + Debug.Assert(typeof(TVectorDouble) == typeof(Vector512)); + result = ((TVectorDouble, TVectorDouble))(object)Vector512.Widen((Vector512)(object)vector); } else { 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 7f73e44ec3ca68..00318b5d15d389 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -155,6 +155,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector128 vector, public static bool EqualsAll(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static bool EqualsAny(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 Equals(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Exp(System.Runtime.Intrinsics.Vector128 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector128 Exp(System.Runtime.Intrinsics.Vector128 vector) { throw null; } [System.CLSCompliantAttribute(false)] public static uint ExtractMostSignificantBits(this System.Runtime.Intrinsics.Vector128 vector) { throw null; } public static System.Runtime.Intrinsics.Vector128 Floor(System.Runtime.Intrinsics.Vector128 vector) { throw null; } @@ -486,6 +488,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector256 vector, public static bool EqualsAll(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static bool EqualsAny(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } public static System.Runtime.Intrinsics.Vector256 Equals(System.Runtime.Intrinsics.Vector256 left, System.Runtime.Intrinsics.Vector256 right) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Exp(System.Runtime.Intrinsics.Vector256 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector256 Exp(System.Runtime.Intrinsics.Vector256 vector) { throw null; } [System.CLSCompliantAttribute(false)] public static uint ExtractMostSignificantBits(this System.Runtime.Intrinsics.Vector256 vector) { throw null; } public static System.Runtime.Intrinsics.Vector256 Floor(System.Runtime.Intrinsics.Vector256 vector) { throw null; } @@ -817,6 +821,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector512 vector, public static bool EqualsAll(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static bool EqualsAny(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Equals(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Exp(System.Runtime.Intrinsics.Vector512 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Exp(System.Runtime.Intrinsics.Vector512 vector) { throw null; } [System.CLSCompliantAttribute(false)] public static ulong ExtractMostSignificantBits(this System.Runtime.Intrinsics.Vector512 vector) { throw null; } public static System.Runtime.Intrinsics.Vector512 Floor(System.Runtime.Intrinsics.Vector512 vector) { throw null; } @@ -1122,6 +1128,8 @@ public static void CopyTo(this System.Runtime.Intrinsics.Vector64 vector, public static bool EqualsAll(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static bool EqualsAny(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static System.Runtime.Intrinsics.Vector64 Equals(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Exp(System.Runtime.Intrinsics.Vector64 vector) { throw null; } + public static System.Runtime.Intrinsics.Vector64 Exp(System.Runtime.Intrinsics.Vector64 vector) { throw null; } [System.CLSCompliantAttribute(false)] public static uint ExtractMostSignificantBits(this System.Runtime.Intrinsics.Vector64 vector) { throw null; } public static System.Runtime.Intrinsics.Vector64 Floor(System.Runtime.Intrinsics.Vector64 vector) { throw null; } diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs index 13ffa0292bff3f..eec2134daf6e6f 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs @@ -4687,6 +4687,24 @@ private static void TestGetOne() Assert.Equal((Vector128)methodInfo.Invoke(null, null), Vector128.Create(T.One)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpDouble), MemberType = typeof(VectorTestMemberData))] + [SkipOnMono("https://github.com/dotnet/runtime/issues/97176")] + public void ExpDoubleTest(double value, double expectedResult, double variance) + { + Vector128 actualResult = Vector128.Exp(Vector128.Create(value)); + AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpSingle), MemberType = typeof(VectorTestMemberData))] + [SkipOnMono("https://github.com/dotnet/runtime/issues/97176")] + public void ExpSingleTest(float value, float expectedResult, float variance) + { + Vector128 actualResult = Vector128.Exp(Vector128.Create(value)); + AssertEqual(Vector128.Create(expectedResult), actualResult, Vector128.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] public void LogDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs index a74bf79d65dea4..3272693a10fedc 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs @@ -5702,6 +5702,24 @@ private static void TestGetOne() Assert.Equal((Vector256)methodInfo.Invoke(null, null), Vector256.Create(T.One)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpDouble), MemberType = typeof(VectorTestMemberData))] + [SkipOnMono("https://github.com/dotnet/runtime/issues/97176")] + public void ExpDoubleTest(double value, double expectedResult, double variance) + { + Vector256 actualResult = Vector256.Exp(Vector256.Create(value)); + AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpSingle), MemberType = typeof(VectorTestMemberData))] + [SkipOnMono("https://github.com/dotnet/runtime/issues/97176")] + public void ExpSingleTest(float value, float expectedResult, float variance) + { + Vector256 actualResult = Vector256.Exp(Vector256.Create(value)); + AssertEqual(Vector256.Create(expectedResult), actualResult, Vector256.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] public void LogDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs index 01c42d277b8553..9192aecfb54c4f 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs @@ -5134,6 +5134,24 @@ private static void TestIsNotSupported() Assert.False((bool)methodInfo.Invoke(null, null)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpDouble), MemberType = typeof(VectorTestMemberData))] + [SkipOnMono("https://github.com/dotnet/runtime/issues/97176")] + public void ExpDoubleTest(double value, double expectedResult, double variance) + { + Vector512 actualResult = Vector512.Exp(Vector512.Create(value)); + AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpSingle), MemberType = typeof(VectorTestMemberData))] + [SkipOnMono("https://github.com/dotnet/runtime/issues/97176")] + public void ExpSingleTest(float value, float expectedResult, float variance) + { + Vector512 actualResult = Vector512.Exp(Vector512.Create(value)); + AssertEqual(Vector512.Create(expectedResult), actualResult, Vector512.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] public void LogDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs index ec5a0e11118cec..0f5a03510daf88 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs @@ -4104,6 +4104,22 @@ private static void TestGetOne() Assert.Equal((Vector64)methodInfo.Invoke(null, null), Vector64.Create(T.One)); } + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpDouble), MemberType = typeof(VectorTestMemberData))] + public void ExpDoubleTest(double value, double expectedResult, double variance) + { + Vector64 actualResult = Vector64.Exp(Vector64.Create(value)); + AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); + } + + [Theory] + [MemberData(nameof(VectorTestMemberData.ExpSingle), MemberType = typeof(VectorTestMemberData))] + public void ExpSingleTest(float value, float expectedResult, float variance) + { + Vector64 actualResult = Vector64.Exp(Vector64.Create(value)); + AssertEqual(Vector64.Create(expectedResult), actualResult, Vector64.Create(variance)); + } + [Theory] [MemberData(nameof(VectorTestMemberData.LogDouble), MemberType = typeof(VectorTestMemberData))] public void LogDoubleTest(double value, double expectedResult, double variance) diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs index cd2a20baa374d7..1579fa54e6af92 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs +++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/VectorTestMemberData.cs @@ -35,6 +35,86 @@ internal static class VectorTestMemberData // use CrossPlatformMachineEpsilon * 10. private const float SingleCrossPlatformMachineEpsilon = 4.76837158e-07f; + public static IEnumerable ExpDouble + { + get + { + yield return new object[] { double.NegativeInfinity, 0.0, 0.0 }; + yield return new object[] { -3.1415926535897932, 0.043213918263772250, DoubleCrossPlatformMachineEpsilon / 10 }; // value: -(pi) + yield return new object[] { -2.7182818284590452, 0.065988035845312537, DoubleCrossPlatformMachineEpsilon / 10 }; // value: -(e) + yield return new object[] { -2.3025850929940457, 0.1, DoubleCrossPlatformMachineEpsilon }; // value: -(ln(10)) + yield return new object[] { -1.5707963267948966, 0.20787957635076191, DoubleCrossPlatformMachineEpsilon }; // value: -(pi / 2) + yield return new object[] { -1.4426950408889634, 0.23629008834452270, DoubleCrossPlatformMachineEpsilon }; // value: -(log2(e)) + yield return new object[] { -1.4142135623730950, 0.24311673443421421, DoubleCrossPlatformMachineEpsilon }; // value: -(sqrt(2)) + yield return new object[] { -1.1283791670955126, 0.32355726390307110, DoubleCrossPlatformMachineEpsilon }; // value: -(2 / sqrt(pi)) + yield return new object[] { -1.0, 0.36787944117144232, DoubleCrossPlatformMachineEpsilon }; + yield return new object[] { -0.78539816339744831, 0.45593812776599624, DoubleCrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { -0.70710678118654752, 0.49306869139523979, DoubleCrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { -0.69314718055994531, 0.5, 0.0 }; // value: -(ln(2)) + yield return new object[] { -0.63661977236758134, 0.52907780826773535, DoubleCrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { -0.43429448190325183, 0.64772148514180065, DoubleCrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { -0.31830988618379067, 0.72737734929521647, DoubleCrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { -0.0, 1.0, 0.0 }; + yield return new object[] { double.NaN, double.NaN, 0.0 }; + yield return new object[] { 0.0, 1.0, 0.0 }; + yield return new object[] { 0.31830988618379067, 1.3748022274393586, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (1 / pi) + yield return new object[] { 0.43429448190325183, 1.5438734439711811, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (log10(e)) + yield return new object[] { 0.63661977236758134, 1.8900811645722220, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (2 / pi) + yield return new object[] { 0.69314718055994531, 2.0, 0.0 }; // value: (ln(2)) + yield return new object[] { 0.70710678118654752, 2.0281149816474725, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (1 / sqrt(2)) + yield return new object[] { 0.78539816339744831, 2.1932800507380155, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (pi / 4) + yield return new object[] { 1.0, 2.7182818284590452, DoubleCrossPlatformMachineEpsilon * 10 }; // expected: (e) + yield return new object[] { 1.1283791670955126, 3.0906430223107976, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (2 / sqrt(pi)) + yield return new object[] { 1.4142135623730950, 4.1132503787829275, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (sqrt(2)) + yield return new object[] { 1.4426950408889634, 4.2320861065570819, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (log2(e)) + yield return new object[] { 1.5707963267948966, 4.8104773809653517, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (pi / 2) + yield return new object[] { 2.3025850929940457, 10.0, DoubleCrossPlatformMachineEpsilon * 10 }; // value: (ln(10)) + yield return new object[] { 2.7182818284590452, 15.154262241479264, DoubleCrossPlatformMachineEpsilon * 100 }; // value: (e) + yield return new object[] { 3.1415926535897932, 23.140692632779269, DoubleCrossPlatformMachineEpsilon * 100 }; // value: (pi) + yield return new object[] { double.PositiveInfinity, double.PositiveInfinity, 0.0 }; + } + } + + public static IEnumerable ExpSingle + { + get + { + yield return new object[] { float.NegativeInfinity, 0.0f, 0.0f }; + yield return new object[] { -3.14159265f, 0.0432139183f, SingleCrossPlatformMachineEpsilon / 10 }; // value: -(pi) + yield return new object[] { -2.71828183f, 0.0659880358f, SingleCrossPlatformMachineEpsilon / 10 }; // value: -(e) + yield return new object[] { -2.30258509f, 0.1f, SingleCrossPlatformMachineEpsilon }; // value: -(ln(10)) + yield return new object[] { -1.57079633f, 0.207879576f, SingleCrossPlatformMachineEpsilon }; // value: -(pi / 2) + yield return new object[] { -1.44269504f, 0.236290088f, SingleCrossPlatformMachineEpsilon }; // value: -(log2(e)) + yield return new object[] { -1.41421356f, 0.243116734f, SingleCrossPlatformMachineEpsilon }; // value: -(sqrt(2)) + yield return new object[] { -1.12837917f, 0.323557264f, SingleCrossPlatformMachineEpsilon }; // value: -(2 / sqrt(pi)) + yield return new object[] { -1.0f, 0.367879441f, SingleCrossPlatformMachineEpsilon }; + yield return new object[] { -0.785398163f, 0.455938128f, SingleCrossPlatformMachineEpsilon }; // value: -(pi / 4) + yield return new object[] { -0.707106781f, 0.493068691f, SingleCrossPlatformMachineEpsilon }; // value: -(1 / sqrt(2)) + yield return new object[] { -0.693147181f, 0.5f, SingleCrossPlatformMachineEpsilon }; // value: -(ln(2)) + yield return new object[] { -0.636619772f, 0.529077808f, SingleCrossPlatformMachineEpsilon }; // value: -(2 / pi) + yield return new object[] { -0.434294482f, 0.647721485f, SingleCrossPlatformMachineEpsilon }; // value: -(log10(e)) + yield return new object[] { -0.318309886f, 0.727377349f, SingleCrossPlatformMachineEpsilon }; // value: -(1 / pi) + yield return new object[] { -0.0f, 1.0f, SingleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { float.NaN, float.NaN, 0.0f }; + yield return new object[] { 0.0f, 1.0f, SingleCrossPlatformMachineEpsilon * 10 }; + yield return new object[] { 0.318309886f, 1.37480223f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (1 / pi) + yield return new object[] { 0.434294482f, 1.54387344f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (log10(e)) + yield return new object[] { 0.636619772f, 1.89008116f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (2 / pi) + yield return new object[] { 0.693147181f, 2.0f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (ln(2)) + yield return new object[] { 0.707106781f, 2.02811498f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (1 / sqrt(2)) + yield return new object[] { 0.785398163f, 2.19328005f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (pi / 4) + yield return new object[] { 1.0f, 2.71828183f, SingleCrossPlatformMachineEpsilon * 10 }; // expected: (e) + yield return new object[] { 1.12837917f, 3.09064302f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (2 / sqrt(pi)) + yield return new object[] { 1.41421356f, 4.11325038f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (sqrt(2)) + yield return new object[] { 1.44269504f, 4.23208611f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (log2(e)) + yield return new object[] { 1.57079633f, 4.81047738f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (pi / 2) + yield return new object[] { 2.30258509f, 10.0f, SingleCrossPlatformMachineEpsilon * 10 }; // value: (ln(10)) + yield return new object[] { 2.71828183f, 15.1542622f, SingleCrossPlatformMachineEpsilon * 100 }; // value: (e) + yield return new object[] { 3.14159265f, 23.1406926f, SingleCrossPlatformMachineEpsilon * 100 }; // value: (pi) + yield return new object[] { float.PositiveInfinity, float.PositiveInfinity, 0.0f }; + } + } + public static IEnumerable LogDouble { get From d05b6c9630efcd06023b62b0941c51608506b125 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 18 Jan 2024 21:44:17 -0800 Subject: [PATCH 116/189] NativeAOT: Ignore preferSize and always expand TLS (#97187) * Always expand TLS for NativeAOT * Update src/coreclr/jit/helperexpansion.cpp --------- Co-authored-by: Jan Kotas --- src/coreclr/jit/helperexpansion.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index b3e5bddd9e804d..9b9fb009a6f50f 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -442,9 +442,15 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess() return result; } - // Always expand for NativeAOT because the TLS access will not be generated by NativeAOT + // Always expand for NativeAOT because the slow TLS access helper will not be generated by NativeAOT const bool isNativeAOT = IsTargetAbi(CORINFO_NATIVEAOT_ABI); - if (!isNativeAOT && opts.OptimizationDisabled()) + if (isNativeAOT) + { + return fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCallNativeAOT>( + false /* expand rarely run blocks for NativeAOT */); + } + + if (opts.OptimizationDisabled()) { JITDUMP("Optimizations aren't allowed - bail out.\n") return result; @@ -459,9 +465,7 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess() return result; } - return isNativeAOT ? fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCallNativeAOT>( - false /* expand rarely run blocks for NativeAOT */) - : fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCall>(true); + return fgExpandHelper<&Compiler::fgExpandThreadLocalAccessForCall>(true); } //------------------------------------------------------------------------------ From 4068acbe4862fd4f8f3758f31a034851ffffc4ce Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 19 Jan 2024 09:10:25 +0100 Subject: [PATCH 117/189] Update eng/common files manually to latest without Arcade itself (#97167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update eng/common files manually to latest without Arcade itself * Use package version from the arcade update Use version number that actually exists. The one we have in `main` doesn't. --------- Co-authored-by: Michal Strehovský --- eng/common/build.cmd | 3 ++ eng/common/build.ps1 | 6 +++ eng/common/build.sh | 21 ++++++++++ eng/common/cross/build-rootfs.sh | 6 +-- eng/common/cross/riscv64/sources.list.sid | 2 +- eng/common/helixpublish.proj | 1 + eng/common/internal/Directory.Build.props | 2 + eng/common/internal/Tools.csproj | 2 + eng/common/sdl/trim-assets-version.ps1 | 2 +- eng/common/templates/job/job.yml | 2 +- .../templates/job/publish-build-assets.yml | 6 ++- .../templates/job/source-index-stage1.yml | 10 ++--- .../templates/post-build/common-variables.yml | 2 +- .../templates/post-build/post-build.yml | 4 +- eng/common/templates/steps/publish-logs.yml | 2 +- eng/common/templates/steps/source-build.yml | 5 ++- eng/common/tools.ps1 | 40 +++++++++++-------- eng/common/tools.sh | 13 ++---- 18 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 eng/common/build.cmd diff --git a/eng/common/build.cmd b/eng/common/build.cmd new file mode 100644 index 00000000000000..99daf368abae74 --- /dev/null +++ b/eng/common/build.cmd @@ -0,0 +1,3 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0build.ps1""" %*" +exit /b %ErrorLevel% diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 33a6f2d0e24811..510458eb35b84b 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -19,6 +19,8 @@ Param( [switch] $pack, [switch] $publish, [switch] $clean, + [switch] $verticalBuild, + [switch][Alias('pb')]$productBuild, [switch][Alias('bl')]$binaryLog, [switch][Alias('nobl')]$excludeCIBinarylog, [switch] $ci, @@ -58,6 +60,8 @@ function Print-Usage() { Write-Host " -sign Sign build outputs" Write-Host " -publish Publish artifacts (e.g. symbols)" Write-Host " -clean Clean the solution" + Write-Host " -verticalBuild Run in 'vertical build' infra mode." + Write-Host " -productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" Write-Host "" Write-Host "Advanced settings:" @@ -120,6 +124,8 @@ function Build { /p:Deploy=$deploy ` /p:Test=$test ` /p:Pack=$pack ` + /p:DotNetBuildRepo=$($productBuild -or $verticalBuild) ` + /p:ArcadeBuildVertical=$verticalBuild ` /p:IntegrationTest=$integrationTest ` /p:PerformanceTest=$performanceTest ` /p:Sign=$sign ` diff --git a/eng/common/build.sh b/eng/common/build.sh index 2c17ba529b913b..2dfb32f2ec4913 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -22,6 +22,9 @@ usage() echo " --sourceBuild Source-build the solution (short: -sb)" echo " Will additionally trigger the following actions: --restore, --build, --pack" echo " If --configuration is not set explicitly, will also set it to 'Release'" + echo " --productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" + echo " Will additionally trigger the following actions: --restore, --build, --pack" + echo " If --configuration is not set explicitly, will also set it to 'Release'" echo " --rebuild Rebuild solution" echo " --test Run all unit tests in the solution (short: -t)" echo " --integrationTest Run all integration tests in the solution" @@ -59,6 +62,8 @@ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" restore=false build=false source_build=false +vertical_build=false +product_build=false rebuild=false test=false integration_test=false @@ -126,6 +131,20 @@ while [[ $# > 0 ]]; do -sourcebuild|-sb) build=true source_build=true + product_build=true + restore=true + pack=true + ;; + -productBuild|-pb) + build=true + product_build=true + restore=true + pack=true + ;; + -verticalbuild|-vb) + build=true + vertical_build=true + product_build=true restore=true pack=true ;; @@ -219,7 +238,9 @@ function Build { /p:RepoRoot="$repo_root" \ /p:Restore=$restore \ /p:Build=$build \ + /p:DotNetBuildRepo=$product_build \ /p:ArcadeBuildFromSource=$source_build \ + /p:ArcadeBuildVertical=$vertical_build \ /p:Rebuild=$rebuild \ /p:Test=$test \ /p:Pack=$pack \ diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 4228f202e5bfa5..9b2791cf568aae 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -182,12 +182,12 @@ while :; do __AlpinePackages="${__AlpinePackages// lldb-dev/}" __QEMUArch=riscv64 __UbuntuArch=riscv64 - __UbuntuRepo="http://deb.debian.org/debian-ports" + __UbuntuRepo="http://deb.debian.org/debian" __UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}" unset __LLDB_Package - if [[ -e "/usr/share/keyrings/debian-ports-archive-keyring.gpg" ]]; then - __Keyring="--keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring" + if [[ -e "/usr/share/keyrings/debian-archive-keyring.gpg" ]]; then + __Keyring="--keyring /usr/share/keyrings/debian-archive-keyring.gpg --include=debian-archive-keyring" fi ;; ppc64le) diff --git a/eng/common/cross/riscv64/sources.list.sid b/eng/common/cross/riscv64/sources.list.sid index 65f730d224caa6..b5f7a7e6e1eb53 100644 --- a/eng/common/cross/riscv64/sources.list.sid +++ b/eng/common/cross/riscv64/sources.list.sid @@ -1 +1 @@ -deb http://deb.debian.org/debian-ports sid main +deb http://deb.debian.org/debian sid main diff --git a/eng/common/helixpublish.proj b/eng/common/helixpublish.proj index d7f185856e7912..c1323bf4121052 100644 --- a/eng/common/helixpublish.proj +++ b/eng/common/helixpublish.proj @@ -1,3 +1,4 @@ + diff --git a/eng/common/internal/Directory.Build.props b/eng/common/internal/Directory.Build.props index dbf99d82a5c2ec..a735fe9a133ca4 100644 --- a/eng/common/internal/Directory.Build.props +++ b/eng/common/internal/Directory.Build.props @@ -1,4 +1,6 @@ + + diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj index 7f5ce6d6081338..8fa77e5b181f89 100644 --- a/eng/common/internal/Tools.csproj +++ b/eng/common/internal/Tools.csproj @@ -1,5 +1,6 @@ + net472 false @@ -27,4 +28,5 @@ + diff --git a/eng/common/sdl/trim-assets-version.ps1 b/eng/common/sdl/trim-assets-version.ps1 index a2e0048770452f..0daa2a9e946289 100644 --- a/eng/common/sdl/trim-assets-version.ps1 +++ b/eng/common/sdl/trim-assets-version.ps1 @@ -72,4 +72,4 @@ catch { Write-Host $_ Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ ExitWithExitCode 1 -} \ No newline at end of file +} diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 0b01531357e3ad..01c0dd995e4b61 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -136,7 +136,7 @@ jobs: condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - task: DownloadPipelineArtifact@2 diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 3b25fd97923c6e..3115990d511474 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -63,6 +63,10 @@ jobs: steps: - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - checkout: self + fetchDepth: 3 + clean: true + - task: DownloadBuildArtifacts@0 displayName: Download artifact inputs: @@ -72,7 +76,7 @@ jobs: condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - task: PowerShell@2 displayName: Publish Build Assets diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b0d40c1879b679..795233662623dc 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -1,6 +1,6 @@ parameters: runAsPublic: false - sourceIndexPackageVersion: 1.1.0.1-20240118.1 + sourceIndexPackageVersion: 1.0.1-20231213.4 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" preSteps: [] @@ -30,20 +30,20 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $(DncEngPublicBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64.open + demands: ImageOverride -equals windows.vs2022.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 + demands: ImageOverride -equals windows.vs2022.amd64 steps: - ${{ each preStep in parameters.preSteps }}: - ${{ preStep }} - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 + displayName: Use .NET 8 SDK inputs: packageType: sdk - version: 6.0.x + version: 8.0.x installationPath: $(Agent.TempDirectory)/dotnet workingDirectory: $(Agent.TempDirectory) diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index 4ef7bd271fbde5..b9ede10bf099ae 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -18,7 +18,7 @@ variables: - name: SymbolToolVersion value: 1.0.1 - name: BinlogToolVersion - value: 1.0.8 + value: 1.0.11 - name: runCodesignValidationInjection value: false diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index d64236b28cfb12..bbc010fe73261e 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -169,7 +169,7 @@ stages: # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 displayName: 'Authenticate to AzDO Feeds' # Signing validation will optionally work with the buildmanifest file which is downloaded from @@ -267,7 +267,7 @@ stages: BARBuildId: ${{ parameters.BARBuildId }} PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - task: PowerShell@2 displayName: Publish Using Darc diff --git a/eng/common/templates/steps/publish-logs.yml b/eng/common/templates/steps/publish-logs.yml index dadf1c464cd1fb..80861297ddc074 100644 --- a/eng/common/templates/steps/publish-logs.yml +++ b/eng/common/templates/steps/publish-logs.yml @@ -3,7 +3,7 @@ parameters: JobLabel: '' CustomSensitiveDataList: '' # A default - in case value from eng/common/templates/post-build/common-variables.yml is not passed - BinlogToolVersion: '1.0.8' + BinlogToolVersion: '1.0.11' steps: - task: Powershell@2 diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml index 41bbb915736a6e..1793eb6beb9f7f 100644 --- a/eng/common/templates/steps/source-build.yml +++ b/eng/common/templates/steps/source-build.yml @@ -94,6 +94,7 @@ steps: $baseOsArgs \ /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ /p:ArcadeBuildFromSource=true \ + /p:DotNetBuildRepo=true \ /p:AssetManifestFileName=$assetManifestFileName displayName: Build @@ -105,7 +106,7 @@ steps: Contents: | **/*.log **/*.binlog - artifacts/source-build/self/prebuilt-report/** + artifacts/sb/prebuilt-report/** TargetFolder: '$(Build.StagingDirectory)/BuildLogs' CleanTargetFolder: true continueOnError: true @@ -126,4 +127,4 @@ steps: - task: ComponentGovernanceComponentDetection@0 displayName: Component Detection (Exclude upstream cache) inputs: - ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache' + ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index e8def7e6a85f21..0da65b5748a7f8 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -165,11 +165,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 } - # Source Build uses DotNetCoreSdkDir variable - if ($env:DotNetCoreSdkDir -ne $null) { - $env:DOTNET_INSTALL_DIR = $env:DotNetCoreSdkDir - } - # Find the first path on %PATH% that contains the dotnet.exe if ($useInstalledDotNetCli -and (-not $globalJsonHasRuntimes) -and ($env:DOTNET_INSTALL_DIR -eq $null)) { $dotnetExecutable = GetExecutableFileName 'dotnet' @@ -601,7 +596,15 @@ function InitializeBuildTool() { ExitWithExitCode 1 } $dotnetPath = Join-Path $dotnetRoot (GetExecutableFileName 'dotnet') - $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'net8.0' } + + # Use override if it exists - commonly set by source-build + if ($null -eq $env:_OverrideArcadeInitializeBuildToolFramework) { + $initializeBuildToolFramework="net9.0" + } else { + $initializeBuildToolFramework=$env:_OverrideArcadeInitializeBuildToolFramework + } + + $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = $initializeBuildToolFramework } } elseif ($msbuildEngine -eq "vs") { try { $msbuildPath = InitializeVisualStudioMSBuild -install:$restore @@ -676,8 +679,14 @@ function Read-ArcadeSdkVersion() { } function InitializeToolset() { - if (Test-Path variable:global:_ToolsetBuildProj) { - return $global:_ToolsetBuildProj + # For Unified Build/Source-build support, check whether the environment variable is + # set. If it is, then use this as the toolset build project. + if ($env:_InitializeToolset -ne $null) { + return $global:_InitializeToolset = $env:_InitializeToolset + } + + if (Test-Path variable:global:_InitializeToolset) { + return $global:_InitializeToolset } $nugetCache = GetNuGetPackageCachePath @@ -688,7 +697,7 @@ function InitializeToolset() { if (Test-Path $toolsetLocationFile) { $path = Get-Content $toolsetLocationFile -TotalCount 1 if (Test-Path $path) { - return $global:_ToolsetBuildProj = $path + return $global:_InitializeToolset = $path } } @@ -711,7 +720,7 @@ function InitializeToolset() { throw "Invalid toolset path: $path" } - return $global:_ToolsetBuildProj = $path + return $global:_InitializeToolset = $path } function ExitWithExitCode([int] $exitCode) { @@ -763,12 +772,10 @@ function MSBuild() { # new scripts need to work with old packages, so we need to look for the old names/versions (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.ArcadeLogging.dll')), (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.Arcade.Sdk.dll')), - (Join-Path $basePath (Join-Path netcoreapp2.1 'Microsoft.DotNet.ArcadeLogging.dll')), - (Join-Path $basePath (Join-Path netcoreapp2.1 'Microsoft.DotNet.Arcade.Sdk.dll')) - (Join-Path $basePath (Join-Path netcoreapp3.1 'Microsoft.DotNet.ArcadeLogging.dll')), - (Join-Path $basePath (Join-Path netcoreapp3.1 'Microsoft.DotNet.Arcade.Sdk.dll')) (Join-Path $basePath (Join-Path net7.0 'Microsoft.DotNet.ArcadeLogging.dll')), - (Join-Path $basePath (Join-Path net7.0 'Microsoft.DotNet.Arcade.Sdk.dll')) + (Join-Path $basePath (Join-Path net7.0 'Microsoft.DotNet.Arcade.Sdk.dll')), + (Join-Path $basePath (Join-Path net8.0 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path net8.0 'Microsoft.DotNet.Arcade.Sdk.dll')) ) $selectedPath = $null foreach ($path in $possiblePaths) { @@ -827,7 +834,8 @@ function MSBuild-Core() { } } - $env:ARCADE_BUILD_TOOL_COMMAND = "$($buildTool.Path) $cmdArgs" + # Be sure quote the path in case there are spaces in the dotnet installation location. + $env:ARCADE_BUILD_TOOL_COMMAND = "`"$($buildTool.Path)`" $cmdArgs" $exitCode = Exec-Process $buildTool.Path $cmdArgs diff --git a/eng/common/tools.sh b/eng/common/tools.sh index e98daf50c6b674..ece4b730795360 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -123,11 +123,6 @@ function InitializeDotNetCli { # so it doesn't output warnings to the console. export LTTNG_HOME="$HOME" - # Source Build uses DotNetCoreSdkDir variable - if [[ -n "${DotNetCoreSdkDir:-}" ]]; then - export DOTNET_INSTALL_DIR="$DotNetCoreSdkDir" - fi - # Find the first path on $PATH that contains the dotnet.exe if [[ "$use_installed_dotnet_cli" == true && $global_json_has_runtimes == false && -z "${DOTNET_INSTALL_DIR:-}" ]]; then local dotnet_path=`command -v dotnet` @@ -343,7 +338,7 @@ function InitializeBuildTool { _InitializeBuildToolCommand="msbuild" # use override if it exists - commonly set by source-build if [[ "${_OverrideArcadeInitializeBuildToolFramework:-x}" == "x" ]]; then - _InitializeBuildToolFramework="net8.0" + _InitializeBuildToolFramework="net9.0" else _InitializeBuildToolFramework="${_OverrideArcadeInitializeBuildToolFramework}" fi @@ -458,12 +453,10 @@ function MSBuild { local possiblePaths=() possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.ArcadeLogging.dll" ) possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.Arcade.Sdk.dll" ) possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.ArcadeLogging.dll" ) possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.Arcade.Sdk.dll" ) for path in "${possiblePaths[@]}"; do if [[ -f $path ]]; then selectedPath=$path From f6824bc9e8c68d3edb3d0a4c91e9bacd73d7767f Mon Sep 17 00:00:00 2001 From: Matous Kozak <55735845+matouskozak@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:48:36 +0100 Subject: [PATCH 118/189] [mono] [tests] Set error for mono_fullaot runtime tests when no suitable tests are found (#96902) --- src/tests/build.proj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tests/build.proj b/src/tests/build.proj index f543dbe85bd81e..3592d7c0761313 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -115,10 +115,15 @@ + + + - + From a18d8cdab4c7c379cc124dbc853e22ab760724c8 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 19 Jan 2024 11:21:11 +0100 Subject: [PATCH 119/189] JIT: Compact newly recognized loops (#97149) --- src/coreclr/jit/optimizer.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index bc4f133f55a010..76c3662e9a799b 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -4409,11 +4409,6 @@ bool Compiler::optCompactLoops() bool changed = false; for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) { - if (!loop->GetHeader()->HasFlag(BBF_OLD_LOOP_HEADER_QUIRK)) - { - continue; - } - changed |= optCompactLoop(loop); } @@ -4447,6 +4442,16 @@ bool Compiler::optCompactLoop(FlowGraphNaturalLoop* loop) continue; } + // If this is a CALLFINALLYRET that is not in the loop, but the + // CALLFINALLY was, then we have to leave it in place. For compaction + // purposes this doesn't really make any difference, since no codegen + // is associated with the CALLFINALLYRET anyway. + if (cur->isBBCallFinallyPairTail()) + { + cur = cur->Next(); + continue; + } + BasicBlock* lastNonLoopBlock = cur; while (true) { From fa94b88777c02d1d85d35798e7f03dcd369a081f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire?= Date: Fri, 19 Jan 2024 06:16:51 -0500 Subject: [PATCH 120/189] Add note about docker in the testing doc (fix #95436) (#95557) * Add note about docker in the testing doc (fix #95436) * Update testing.md --- docs/workflow/testing/libraries/testing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/workflow/testing/libraries/testing.md b/docs/workflow/testing/libraries/testing.md index 7eeba3bc257e6a..0c32053356c4e6 100644 --- a/docs/workflow/testing/libraries/testing.md +++ b/docs/workflow/testing/libraries/testing.md @@ -79,6 +79,9 @@ cd src\libraries\System.Collections.Immutable\tests dotnet build /t:Test ``` +**NOTE**: if your environment doesn't have the required SDK installed (e.g. inside [Docker container](/docs/workflow/building/coreclr/linux-instructions.md#build-using-docker)), +use `./dotnet.sh`/`.\dotnet.cmd` instead of `dotnet`. + ### Running only certain tests It is possible to pass parameters to the underlying xunit runner via the `XUnitOptions` parameter, e.g., to filter to tests in just one fixture (class): From b88023a1c333afcbbe86834bb14d28d5c980c27e Mon Sep 17 00:00:00 2001 From: Matin Mohammadi Date: Fri, 19 Jan 2024 14:54:10 +0330 Subject: [PATCH 121/189] Update README.md and replace .NET Cores with .NET (#97197) * Update README.md and replace .NET Cores with .NET * Apply suggestions from code review --------- Co-authored-by: Jan Kotas --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c18afe019a852b..1d3328b81b2d66 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ We welcome contributions! Many people all over the world have helped make this p * [Contributing](CONTRIBUTING.md) explains what kinds of contributions we welcome * [Workflow Instructions](docs/workflow/README.md) explains how to build and test -* [Get Up and Running on .NET Core](docs/project/dogfooding.md) explains how to get nightly builds of the runtime and its libraries to test them in your own projects. +* [Dogfooding .NET](docs/project/dogfooding.md) explains how to get nightly builds of the runtime and its libraries to test them in your own projects. ## Reporting security issues and security bugs Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) . You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://www.microsoft.com/msrc/faqs-report-an-issue). You can also find these instructions in this repo's [Security doc](SECURITY.md). -Also see info about related [Microsoft .NET Core and ASP.NET Core Bug Bounty Program](https://www.microsoft.com/msrc/bounty-dot-net-core). +Also see info about related [Microsoft .NET Bounty Program](https://www.microsoft.com/msrc/bounty-dot-net-core). ## Filing issues @@ -52,7 +52,7 @@ For other issues, please file them to their appropriate sibling repos. We have l ## Useful Links -* [.NET Core source index](https://source.dot.net) / [.NET Framework source index](https://referencesource.microsoft.com) +* [.NET source index](https://source.dot.net) / [.NET Framework source index](https://referencesource.microsoft.com) * [API Reference docs](https://docs.microsoft.com/dotnet/api) * [.NET API Catalog](https://apisof.net) (incl. APIs from daily builds and API usage info) * [API docs writing guidelines](https://github.com/dotnet/dotnet-api-docs/wiki) - useful when writing /// comments From 8c73f0f7129aae439e7da095c7b3f25e7209bb76 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 19 Jan 2024 14:28:33 +0100 Subject: [PATCH 122/189] Disable dump creation for ParallelCrashTester child (#97095) The child process is intentionally crashing, so creating a dump for it is a waste of time and disk space and it also seems to be causing random failures of the test in the CI. This change disables the dump creation for it. --- src/tests/baseservices/exceptions/simple/ParallelCrashTester.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/baseservices/exceptions/simple/ParallelCrashTester.cs b/src/tests/baseservices/exceptions/simple/ParallelCrashTester.cs index 5d3124965d84e0..25ecdc5eb883a1 100644 --- a/src/tests/baseservices/exceptions/simple/ParallelCrashTester.cs +++ b/src/tests/baseservices/exceptions/simple/ParallelCrashTester.cs @@ -35,6 +35,8 @@ private static void RunParallelCrash(int arg) testProcess.StartInfo.FileName = Path.Combine(Environment.GetEnvironmentVariable("CORE_ROOT"), "corerun"); testProcess.StartInfo.Arguments = $"ParallelCrash.dll {arg}"; testProcess.StartInfo.UseShellExecute = false; + // Disable creating dump since the target process is expected to crash + testProcess.StartInfo.Environment.Remove("DOTNET_DbgEnableMiniDump"); testProcess.Start(); testProcess.WaitForExit(); From aca77bdbef56e9be27d93a4636e65c417dc197e4 Mon Sep 17 00:00:00 2001 From: Alan Hayward Date: Fri, 19 Jan 2024 14:30:18 +0000 Subject: [PATCH 123/189] Use insGetPredicateType for SVE encodings (#97142) * Use insGetPredicateType for SVE encodings * Add predicate as counter printing * Pass type to emitPredicateRegName --- src/coreclr/jit/emitarm64.cpp | 193 +++++++++++++++++++--------------- src/coreclr/jit/emitarm64.h | 14 +-- 2 files changed, 114 insertions(+), 93 deletions(-) diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 1877f0a9036e9f..5c9fb990bb8591 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1930,6 +1930,14 @@ static const char * const pRegNames[] = "p10", "p11", "p12", "p13", "p14", "p15" }; + +static const char * const pnRegNames[] = +{ + "pn0", "pn1", "pn2", "pn3", "pn4", + "pn5", "pn6", "pn7", "pn8", "pn9", + "pn10", "pn11", "pn12", "pn13", "pn14", + "pn15" +}; // clang-format on //------------------------------------------------------------------------ @@ -2027,13 +2035,13 @@ const char* emitter::emitVectorRegName(regNumber reg) // Return value: // A string that represents a predicate register name. // -const char* emitter::emitPredicateRegName(regNumber reg) +const char* emitter::emitPredicateRegName(regNumber reg, PredicateType ptype) { assert((reg >= REG_P0) && (reg <= REG_P15)); int index = (int)reg - (int)REG_P0; - return pRegNames[index]; + return (ptype == PREDICATE_N_SIZED) ? pnRegNames[index] : pRegNames[index]; } /***************************************************************************** @@ -14065,7 +14073,7 @@ void emitter::emitIns_Call(EmitCallType callType, * Returns the predicate type for the given SVE format. */ -/*static*/ emitter::PredicateType emitter::insGetPredicateType(insFormat fmt) +/*static*/ emitter::PredicateType emitter::insGetPredicateType(insFormat fmt, int regpos /* =0 */) { switch (fmt) { @@ -14107,13 +14115,8 @@ void emitter::emitIns_Call(EmitCallType callType, case IF_SVE_IF_4A_A: case IF_SVE_IM_3A: case IF_SVE_IN_4A: - case IF_SVE_CX_4A: - case IF_SVE_CX_4A_A: - case IF_SVE_CY_3A: - case IF_SVE_CY_3B: case IF_SVE_IX_4A: case IF_SVE_HI_3A: - case IF_SVE_HT_4A: case IF_SVE_DG_2A: case IF_SVE_IO_3A: case IF_SVE_IP_4A: @@ -14124,7 +14127,6 @@ void emitter::emitIns_Call(EmitCallType callType, case IF_SVE_DA_4A: case IF_SVE_DB_3B: case IF_SVE_DC_3A: - case IF_SVE_GE_4A: case IF_SVE_GI_4A: case IF_SVE_IC_3A_C: case IF_SVE_IC_3A: @@ -14197,21 +14199,29 @@ void emitter::emitIns_Call(EmitCallType callType, case IF_SVE_CF_2C: case IF_SVE_CF_2D: case IF_SVE_CI_3A: - case IF_SVE_DL_2A: case IF_SVE_DM_2A: case IF_SVE_DN_2A: case IF_SVE_DO_2A: case IF_SVE_DP_2A: + case IF_SVE_DR_1A: + case IF_SVE_DT_3A: + case IF_SVE_DU_3A: case IF_SVE_CK_2A: case IF_SVE_DI_2A: return PREDICATE_SIZED; + case IF_SVE_DL_2A: + case IF_SVE_DY_3A: + case IF_SVE_DZ_1A: + return PREDICATE_N_SIZED; + // This is a special case as the second register could be ZERO or MERGE. // / // Therefore, by default return NONE due to ambiguity. case IF_SVE_AH_3A: case IF_SVE_DB_3A: // TODO: Handle these cases. + assert(false); break; case IF_SVE_JD_4B: @@ -14274,6 +14284,15 @@ void emitter::emitIns_Call(EmitCallType callType, case IF_SVE_IY_4A: return PREDICATE_NONE; + case IF_SVE_CX_4A: + case IF_SVE_CX_4A_A: + case IF_SVE_CY_3A: + case IF_SVE_CY_3B: + case IF_SVE_GE_4A: + case IF_SVE_HT_4A: + assert((regpos == 1) || (regpos == 2)); + return (regpos == 1 ? PREDICATE_SIZED : PREDICATE_ZERO); + default: break; } @@ -17502,7 +17521,7 @@ void emitter::emitDispSveConsecutiveRegList(regNumber firstReg, unsigned listSiz void emitter::emitDispPredicateReg(regNumber reg, PredicateType ptype, insOpts opt, bool addComma) { assert(isPredicateRegister(reg)); - printf(emitPredicateRegName(reg)); + printf(emitPredicateRegName(reg, ptype)); if (ptype == PREDICATE_MERGE) { @@ -17512,7 +17531,7 @@ void emitter::emitDispPredicateReg(regNumber reg, PredicateType ptype, insOpts o { printf("/z"); } - else if (ptype == PREDICATE_SIZED) + else if (ptype == PREDICATE_SIZED || ptype == PREDICATE_N_SIZED) { emitDispElemsize(optGetSveElemsize(opt)); } @@ -19188,10 +19207,10 @@ void emitter::emitDispInsHelp( // (predicated) case IF_SVE_GR_3A: // ........xx...... ...gggmmmmmddddd -- SVE2 floating-point pairwise operations case IF_SVE_HL_3A: // ........xx...... ...gggmmmmmddddd -- SVE floating-point arithmetic (predicated) - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // ., /, . @@ -19206,18 +19225,18 @@ void emitter::emitDispInsHelp( // ., /M, ., # case IF_SVE_AM_2A: // ........xx...... ...gggxxiiiddddd -- SVE bitwise shift by immediate (predicated) - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispImm(emitGetInsSC(id), false); // iiii + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispImm(emitGetInsSC(id), false); // iiii break; // ., /M, ., .D case IF_SVE_AO_3A: // ........xx...... ...gggmmmmmddddd -- SVE bitwise shift by wide elements (predicated) - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispSveReg(id->idReg3(), INS_OPTS_SCALABLE_D, false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg3(), INS_OPTS_SCALABLE_D, false); // mmmmm break; // ., /M, ., . @@ -19226,8 +19245,8 @@ void emitter::emitDispInsHelp( // (predicated) case IF_SVE_AS_4A: // ........xx.mmmmm ...gggaaaaaddddd -- SVE integer multiply-add writing multiplicand // (predicated) - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg emitDispSveReg(id->idReg3(), id->idInsOpt(), true); emitDispSveReg(id->idReg4(), id->idInsOpt(), false); break; @@ -19260,10 +19279,10 @@ void emitter::emitDispInsHelp( // ., , ., . case IF_SVE_CM_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally broadcast element to vector - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // , , , . @@ -19271,10 +19290,10 @@ void emitter::emitDispInsHelp( case IF_SVE_CN_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally extract element to SIMD&FP scalar case IF_SVE_CO_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally extract element to general register case IF_SVE_HJ_3A: // ........xx...... ...gggmmmmmddddd -- SVE floating-point serial reduction (predicated) - emitDispReg(id->idReg1(), size, true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg - emitDispReg(id->idReg1(), size, true); // ddddd - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispReg(id->idReg1(), size, true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispReg(id->idReg1(), size, true); // ddddd + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // , , . @@ -19284,9 +19303,9 @@ void emitter::emitDispInsHelp( case IF_SVE_CR_3A: // ........xx...... ...gggnnnnnddddd -- SVE extract element to SIMD&FP scalar register case IF_SVE_CS_3A: // ........xx...... ...gggnnnnnddddd -- SVE extract element to general register case IF_SVE_HE_3A: // ........xx...... ...gggnnnnnddddd -- SVE floating-point recursive reduction - emitDispReg(id->idReg1(), size, true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispReg(id->idReg1(), size, true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // ., , . @@ -19295,15 +19314,15 @@ void emitter::emitDispInsHelp( case IF_SVE_AL_3A: // ........xx...... ...gggnnnnnddddd -- SVE integer min/max reduction (quadwords) case IF_SVE_GS_3A: // ........xx...... ...gggnnnnnddddd -- SVE floating-point recursive reduction (quadwords) emitDispVectorReg(id->idReg1(), optSveToQuadwordElemsizeArrangement(id->idInsOpt()), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; //
, , . case IF_SVE_AI_3A: // ........xx...... ...gggnnnnnddddd -- SVE integer add reduction (predicated) - emitDispReg(id->idReg1(), EA_8BYTE, true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispReg(id->idReg1(), EA_8BYTE, true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // ., /M, . @@ -19313,65 +19332,65 @@ void emitter::emitDispInsHelp( case IF_SVE_ES_3A: // ........xx...... ...gggnnnnnddddd -- SVE2 integer unary operations (predicated) case IF_SVE_HQ_3A: // ........xx...... ...gggnnnnnddddd -- SVE floating-point round to integral value case IF_SVE_HR_3A: // ........xx...... ...gggnnnnnddddd -- SVE floating-point unary operations - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // ., , . case IF_SVE_CL_3A: // ........xx...... ...gggnnnnnddddd -- SVE compress active elements - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; // ., /M, case IF_SVE_CP_3A: // ........xx...... ...gggnnnnnddddd -- SVE copy SIMD&FP scalar register to vector // (predicated) - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispReg(id->idReg3(), size, false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispReg(id->idReg3(), size, false); // mmmmm break; // ., /M, case IF_SVE_CQ_3A: // ........xx...... ...gggnnnnnddddd -- SVE copy general register to vector (predicated) - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispReg(encodingZRtoSP(id->idReg3()), size, false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispReg(encodingZRtoSP(id->idReg3()), size, false); // mmmmm break; // ., /Z, ., . case IF_SVE_CX_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE integer compare vectors case IF_SVE_GE_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE2 character match case IF_SVE_HT_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE floating-point compare vectors - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD - emitDispPredicateReg(id->idReg2(), PREDICATE_ZERO, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn - emitDispSveReg(id->idReg4(), id->idInsOpt(), false); // mmmmm + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt, 1), id->idInsOpt(), true); // DDDD + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt, 2), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg4(), id->idInsOpt(), false); // mmmmm break; // ., /Z, ., .D case IF_SVE_CX_4A_A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE integer compare vectors - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD - emitDispPredicateReg(id->idReg2(), PREDICATE_ZERO, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn - emitDispSveReg(id->idReg4(), INS_OPTS_SCALABLE_D, false); // mmmmm + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt, 1), id->idInsOpt(), true); // DDDD + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt, 2), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg4(), INS_OPTS_SCALABLE_D, false); // mmmmm break; // ., /Z, ., # case IF_SVE_CY_3A: // ........xx.iiiii ...gggnnnnn.DDDD -- SVE integer compare with signed immediate case IF_SVE_CY_3B: // ........xx.iiiii ii.gggnnnnn.DDDD -- SVE integer compare with unsigned immediate - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD - emitDispPredicateReg(id->idReg2(), PREDICATE_ZERO, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn - emitDispImm(emitGetInsSC(id), false, (fmt == IF_SVE_CY_3B)); // iiiii + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt, 1), id->idInsOpt(), true); // DDDD + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt, 2), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), id->idInsOpt(), true); // nnnnn + emitDispImm(emitGetInsSC(id), false, (fmt == IF_SVE_CY_3B)); // iiiii break; // ., /M, . case IF_SVE_EQ_3A: // ........xx...... ...gggnnnnnddddd -- SVE2 integer pairwise add and accumulate long - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispLowPredicateReg(id->idReg2(), PREDICATE_MERGE, id->idInsOpt(), true); // ggg - emitDispSveReg(id->idReg3(), (insOpts)((unsigned)id->idInsOpt() - 1), false); // mmmmm + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispLowPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), (insOpts)((unsigned)id->idInsOpt() - 1), false); // mmmmm break; // .H, { .S-.S }, # @@ -19383,22 +19402,22 @@ void emitter::emitDispInsHelp( // , ., case IF_SVE_DL_2A: // ........xx...... .....l.NNNNddddd -- SVE predicate count (predicate-as-counter) - emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_SIZED, id->idInsOpt(), true); // NNNN + emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // NNNN emitDispVectorLengthSpecifier(id); break; // , . case IF_SVE_DM_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec register by predicate count - emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_SIZED, id->idInsOpt(), false); // MMMM + emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), false); // MMMM break; // ., . case IF_SVE_DN_2A: // ........xx...... .......MMMMddddd -- SVE inc/dec vector by predicate count case IF_SVE_DP_2A: // ........xx...... .......MMMMddddd -- SVE saturating inc/dec vector by predicate count - emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_SIZED, id->idInsOpt(), false); // MMMM + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), false); // MMMM break; // , ., @@ -19408,9 +19427,9 @@ void emitter::emitDispInsHelp( { // 32-bit result: , ., // 64-bit result: , . - const bool is32BitResult = (id->idOpSize() == EA_4BYTE); // X - emitDispReg(id->idReg1(), EA_8BYTE, true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_SIZED, id->idInsOpt(), is32BitResult); // MMMM + const bool is32BitResult = (id->idOpSize() == EA_4BYTE); // X + emitDispReg(id->idReg1(), EA_8BYTE, true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), is32BitResult); // MMMM if (is32BitResult) { @@ -19420,8 +19439,8 @@ void emitter::emitDispInsHelp( else { assert((ins == INS_sve_uqdecp) || (ins == INS_sve_uqincp)); - emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd - emitDispPredicateReg(id->idReg2(), PREDICATE_SIZED, id->idInsOpt(), false); // MMMM + emitDispReg(id->idReg1(), id->idOpSize(), true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), false); // MMMM } break; @@ -19431,7 +19450,7 @@ void emitter::emitDispInsHelp( // .B case IF_SVE_DR_1A: // ................ .......NNNN..... -- SVE FFR write from predicate - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), false); // NNNN + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), false); // NNNN break; // , @@ -19464,9 +19483,9 @@ void emitter::emitDispInsHelp( case IF_SVE_DT_3A: // ........xx.mmmmm ...X..nnnnn.DDDD -- SVE integer compare scalar count and limit // ., , case IF_SVE_DU_3A: // ........xx.mmmmm ......nnnnn.DDDD -- SVE pointer conflict compare - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD - emitDispReg(id->idReg2(), id->idOpSize(), true); // nnnnn - emitDispReg(id->idReg3(), id->idOpSize(), false); // mmmmm + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), true); // DDDD + emitDispReg(id->idReg2(), id->idOpSize(), true); // nnnnn + emitDispReg(id->idReg3(), id->idOpSize(), false); // mmmmm break; // {., .}, , @@ -19480,15 +19499,15 @@ void emitter::emitDispInsHelp( // ., , , case IF_SVE_DY_3A: // ........xx.mmmmm ..l...nnnnn..DDD -- SVE integer compare scalar count and limit // (predicate-as-counter) - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDD - emitDispReg(id->idReg2(), id->idOpSize(), true); // nnnnn - emitDispReg(id->idReg3(), id->idOpSize(), true); // mmmmm + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), true); // DDD + emitDispReg(id->idReg2(), id->idOpSize(), true); // nnnnn + emitDispReg(id->idReg3(), id->idOpSize(), true); // mmmmm emitDispVectorLengthSpecifier(id); break; // PTRUE . case IF_SVE_DZ_1A: // ........xx...... .............DDD -- sve_int_pn_ptrue - emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), false); // DDD + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), false); // DDD break; // FDUP ., # diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index e76290a28a28bd..04f8322a06fc94 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -21,15 +21,16 @@ static bool strictArmAsm; enum PredicateType { - PREDICATE_NONE = 0, - PREDICATE_MERGE, - PREDICATE_ZERO, - PREDICATE_SIZED, + PREDICATE_NONE = 0, // Predicate printed with no extensions + PREDICATE_MERGE, // Predicate printed with /m + PREDICATE_ZERO, // Predicate printed with /z + PREDICATE_SIZED, // Predicate printed with element size + PREDICATE_N_SIZED, // Predicate printed printed as counter with element size }; const char* emitSveRegName(regNumber reg); const char* emitVectorRegName(regNumber reg); -const char* emitPredicateRegName(regNumber reg); +const char* emitPredicateRegName(regNumber reg, PredicateType ptype); void emitDispInsHelp( instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig); @@ -491,7 +492,8 @@ static code_t insEncodeSveElemsize_tszh_22_tszl_20_to_19(emitAttr size); static int insGetSveReg1ListSize(instruction ins); // Returns the predicate type for the given SVE format. -static PredicateType insGetPredicateType(insFormat fmt); +// Register position is required for instructions with multiple predicates. +static PredicateType insGetPredicateType(insFormat fmt, int regpos = 0); // Returns true if the specified instruction can encode the 'dtype' field. static bool canEncodeSveElemsize_dtype(instruction ins); From 8c3ee305d714faa4d669477efea4f0d847209ee3 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 19 Jan 2024 16:23:05 +0100 Subject: [PATCH 124/189] [browser] remove legacy js interop (#96841) --- .gitattributes | 1 - eng/liveBuilds.targets | 1 - eng/testing/tests.browser.targets | 16 - .../Directory.Build.props | 1 - ...tem.Runtime.InteropServices.JavaScript.sln | 2 - .../src/CompatibilitySuppressions.xml | 37 - ....Runtime.InteropServices.JavaScript.csproj | 15 - .../JavaScript/Interop/JavaScriptImports.cs | 37 - .../JavaScript/Interop/LegacyExports.cs | 247 ------ .../JavaScript/JSFunctionBinding.cs | 2 - .../JavaScript/JSObject.References.cs | 39 - .../JavaScript/JSProxyContext.cs | 63 -- .../JavaScript/Legacy/Array.cs | 122 --- .../JavaScript/Legacy/ArrayBuffer.cs | 56 -- .../JavaScript/Legacy/DataView.cs | 233 ------ .../JavaScript/Legacy/Function.cs | 66 -- .../Legacy/LegacyHostImplementation.cs | 226 ----- .../JavaScript/Legacy/Runtime.cs | 117 --- .../JavaScript/Legacy/Uint8Array.cs | 84 -- ...ropServices.JavaScript.Legacy.Tests.csproj | 38 - .../InteropServices/JavaScript/ArrayTests.cs | 173 ---- .../JavaScript/DataViewTests.cs | 134 --- .../JavaScript/DelegateTests.cs | 449 ---------- .../JavaScript/HelperMarshal.cs | 463 ---------- .../JavaScript/JavaScriptTests.cs | 215 ----- .../JavaScript/MarshalTests.cs | 791 ------------------ .../InteropServices/JavaScript/MemoryTests.cs | 144 ---- .../JavaScript/ParallelTests.cs | 70 -- .../JavaScript/TypedArrayTests.cs | 38 - ...me.InteropServices.JavaScript.Tests.csproj | 5 +- .../JavaScript}/HttpRequestMessageTest.cs | 83 -- .../JavaScript/JSImportExportTest.cs | 5 - .../InteropServices/JavaScript/TimerTests.cs | 0 .../InteropServices/JavaScript}/timers.mjs | 0 src/libraries/tests.proj | 1 - src/mono/browser/.gitignore | 1 - src/mono/browser/browser.proj | 9 +- src/mono/browser/build/BrowserWasmApp.targets | 7 - .../ILLink.Substitutions.LegacyJsInterop.xml | 7 - src/mono/browser/build/README.md | 2 - src/mono/browser/runtime/CMakeLists.txt | 1 - src/mono/browser/runtime/corebindings.c | 30 - src/mono/browser/runtime/cwraps.ts | 49 +- src/mono/browser/runtime/dotnet-legacy.d.ts | 295 ------- src/mono/browser/runtime/dotnet.d.ts | 27 - src/mono/browser/runtime/driver.c | 551 +----------- .../browser/runtime/es6/dotnet.es6.lib.js | 6 - src/mono/browser/runtime/exports-binding.ts | 24 - src/mono/browser/runtime/exports-linker.ts | 7 +- src/mono/browser/runtime/exports.ts | 75 +- src/mono/browser/runtime/globals.ts | 4 +- src/mono/browser/runtime/loader/globals.ts | 1 - .../browser/runtime/net6-legacy/buffers.ts | 114 --- .../runtime/net6-legacy/corebindings.ts | 115 --- .../browser/runtime/net6-legacy/cs-to-js.ts | 354 -------- .../runtime/net6-legacy/export-types.ts | 243 ------ .../runtime/net6-legacy/exports-legacy.ts | 115 --- .../browser/runtime/net6-legacy/globals.ts | 37 - .../browser/runtime/net6-legacy/js-to-cs.ts | 294 ------- .../runtime/net6-legacy/method-binding.ts | 682 --------------- .../runtime/net6-legacy/method-calls.ts | 307 ------- .../browser/runtime/net6-legacy/strings.ts | 40 - src/mono/browser/runtime/rollup.config.js | 25 +- src/mono/browser/runtime/startup.ts | 33 +- src/mono/browser/runtime/tsconfig.shared.json | 1 - src/mono/browser/runtime/types/emscripten.ts | 19 - src/mono/browser/runtime/types/index.ts | 10 - src/mono/browser/runtime/types/internal.ts | 11 +- src/mono/browser/runtime/wasm-config.h.in | 3 - .../WorkloadManifest.targets.in | 1 - .../Wasm.Advanced.Sample.csproj | 1 - .../sample/wasm/browser-bench/JSInterop.cs | 45 - src/mono/sample/wasm/browser-bench/main.js | 26 +- .../Wasm.Browser.WebPack.Sample.csproj | 2 - .../Wasm.Console.Node.TS.Sample.csproj | 2 - .../Wasm.Node.WebPack.Sample.csproj | 2 - .../Blazor/WorkloadRequiredTests.cs | 1 - .../WasmNativeDefaultsTests.cs | 9 +- src/mono/wasm/build/README.md | 2 - src/mono/wasm/build/WasmApp.Common.targets | 1 - src/mono/wasm/test-main.js | 30 +- 81 files changed, 65 insertions(+), 7530 deletions(-) delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ArrayTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DataViewTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MemoryTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ParallelTests.cs delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/TypedArrayTests.cs rename src/libraries/System.Runtime.InteropServices.JavaScript/tests/{System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/Http => System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript}/HttpRequestMessageTest.cs (77%) rename src/libraries/System.Runtime.InteropServices.JavaScript/tests/{System.Runtime.InteropServices.JavaScript.Legacy.UnitTests => System.Runtime.InteropServices.JavaScript.UnitTests}/System/Runtime/InteropServices/JavaScript/TimerTests.cs (100%) rename src/libraries/System.Runtime.InteropServices.JavaScript/tests/{System.Runtime.InteropServices.JavaScript.Legacy.UnitTests => System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript}/timers.mjs (100%) delete mode 100644 src/mono/browser/build/ILLink.Substitutions.LegacyJsInterop.xml delete mode 100644 src/mono/browser/runtime/dotnet-legacy.d.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/buffers.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/corebindings.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/cs-to-js.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/export-types.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/exports-legacy.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/globals.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/js-to-cs.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/method-binding.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/method-calls.ts delete mode 100644 src/mono/browser/runtime/net6-legacy/strings.ts diff --git a/.gitattributes b/.gitattributes index f94bdb5eefa124..55a35b1afffff0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -78,4 +78,3 @@ src/tests/JIT/Performance/CodeQuality/BenchmarksGame/reverse-complement/revcomp- src/tests/JIT/Performance/CodeQuality/BenchmarksGame/k-nucleotide/knucleotide-input.txt text eol=lf src/tests/JIT/Performance/CodeQuality/BenchmarksGame/k-nucleotide/knucleotide-input-big.txt text eol=lf src/mono/wasm/runtime/dotnet.d.ts text eol=lf -src/mono/wasm/runtime/dotnet-legacy.d.ts text eol=lf diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 5f3bcc6c356f13..70a3cac8cbcdd9 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -204,7 +204,6 @@ $(LibrariesNativeArtifactsPath)dotnet.runtime.js; $(LibrariesNativeArtifactsPath)dotnet.runtime.js.map; $(LibrariesNativeArtifactsPath)dotnet.d.ts; - $(LibrariesNativeArtifactsPath)dotnet-legacy.d.ts; $(LibrariesNativeArtifactsPath)package.json; $(LibrariesNativeArtifactsPath)dotnet.native.wasm; $(LibrariesNativeArtifactsPath)dotnet.native.js.symbols; diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index 005a43083a823f..5cb92cf605c6d1 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -33,7 +33,6 @@ >true <_BundleAOTTestWasmAppForHelixDependsOn>$(_BundleAOTTestWasmAppForHelixDependsOn);PrepareForWasmBuildApp;_PrepareForAOTOnHelix - true false true true @@ -47,21 +46,6 @@ true - - - <_ExtraTrimmerArgs Condition="'$(WasmEnableLegacyJsInterop)' == 'false'">$(_ExtraTrimmerArgs) --substitutions "$(BrowserProjectRoot)build\ILLink.Substitutions.LegacyJsInterop.xml" - - - - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln index 73c85be0abf1e4..a13a1af6f03fce 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JSImportGenerator.Unit.Tests", "tests\JSImportGenerator.UnitTest\JSImportGenerator.Unit.Tests.csproj", "{BFED925C-18F2-4C98-833E-66F205234598}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript.Legacy.Tests", "tests\System.Runtime.InteropServices.JavaScript.Legacy.UnitTests\System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj", "{ABA5A92B-CAD8-47E8-A7CE-D28A67FB69C0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript.Tests", "tests\System.Runtime.InteropServices.JavaScript.UnitTests\System.Runtime.InteropServices.JavaScript.Tests.csproj", "{765B4AA5-723A-44FF-BC4E-EB0F03103F6D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComInterfaceGenerator", "..\System.Runtime.InteropServices\gen\ComInterfaceGenerator\ComInterfaceGenerator.csproj", "{FB12C247-AFEF-4772-BB0C-983969B6CF32}" diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml index 74336f2b956571..5619a7b22a8f20 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml @@ -1,42 +1,5 @@  - - CP0001 - T:System.Runtime.InteropServices.JavaScript.Array - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - - CP0001 - T:System.Runtime.InteropServices.JavaScript.ArrayBuffer - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - - CP0001 - T:System.Runtime.InteropServices.JavaScript.DataView - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - - CP0001 - T:System.Runtime.InteropServices.JavaScript.Function - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - - CP0001 - T:System.Runtime.InteropServices.JavaScript.Runtime - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - - CP0001 - T:System.Runtime.InteropServices.JavaScript.Uint8Array - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - CP0001 T:System.Runtime.InteropServices.JavaScript.SynchronizationContextExtension diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 1b8664dc435917..8ad195d25ad0f5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -14,9 +14,7 @@ true true false - true $(DefineConstants);FEATURE_WASM_THREADS - $(DefineConstants);DISABLE_LEGACY_JS_INTEROP $(DefineConstants);ENABLE_JS_INTEROP_BY_VALUE true @@ -29,7 +27,6 @@ - @@ -68,18 +65,6 @@ - - - - - - - - - - - - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs deleted file mode 100644 index 667fed536adaf4..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Runtime.InteropServices.JavaScript -{ - internal static unsafe partial class JavaScriptImports - { -#if !DISABLE_LEGACY_JS_INTEROP - #region legacy - - public static object GetGlobalObject(string? str = null) - { - int exception; - Interop.Runtime.GetGlobalObjectRef(str, out exception, out object jsObj); - - if (exception != 0) - throw new JSException(SR.Format(SR.ErrorResolvingFromGlobalThis, str)); - - LegacyHostImplementation.ReleaseInFlight(jsObj); - return jsObj; - } - - public static IntPtr CreateCSOwnedObject(string typeName, object[] parms) - { - Interop.Runtime.CreateCSOwnedObjectRef(typeName, parms, out int exception, out object res); - if (exception != 0) - throw new JSException((string)res); - - return (IntPtr)(int)res; - } - - #endregion -#endif - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs deleted file mode 100644 index 928d30f74f513c..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/LegacyExports.cs +++ /dev/null @@ -1,247 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace System.Runtime.InteropServices.JavaScript -{ - internal static unsafe partial class LegacyExportsTrimmingRoot - { - // the public methods are used from JavaScript, but the trimmer doesn't know about it. - // It's protected by DynamicDependencyAttribute on JSFunctionBinding.BindJSFunction. - public static void TrimWhenNotWasmEnableLegacyJsInterop() - { - // if MSBuild property WasmEnableLegacyJsInterop==false this call would be substituted away and LegacyExports would be trimmed. - LegacyExports.PreventTrimming(); - } - } - - internal static unsafe partial class LegacyExports - { - // the public methods of this class are used from JavaScript, but the trimmer doesn't know about it. - // They are protected by LegacyExportsTrimmingRoot.PreventTrimming and JSFunctionBinding.BindJSFunction. - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(LegacyExports))] - internal static void PreventTrimming() - { - } - - public static void GetCSOwnedObjectByJSHandleRef(nint jsHandle, int shouldAddInflight, out JSObject? result) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - result = JSProxyContext.MainThreadContext.GetCSOwnedObjectByJSHandle(jsHandle, shouldAddInflight); - } - - public static IntPtr GetCSOwnedObjectJSHandleRef(in JSObject jsObject, int shouldAddInflight) - { - jsObject.AssertNotDisposed(); - - if (shouldAddInflight != 0) - { - jsObject.AddInFlight(); - } - return jsObject.JSHandle; - } - - public static IntPtr TryGetCSOwnedObjectJSHandleRef(in object rawObj, int shouldAddInflight) - { - JSObject? jsObject = rawObj as JSObject; - if (jsObject != null && shouldAddInflight != 0) - { - jsObject.AddInFlight(); - } - return jsObject?.JSHandle ?? IntPtr.Zero; - } - - public static void CreateCSOwnedProxyRef(nint jsHandle, LegacyHostImplementation.MappedType mappedType, int shouldAddInflight, out JSObject jsObject) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - jsObject = JSProxyContext.MainThreadContext.CreateCSOwnedProxy(jsHandle, mappedType, shouldAddInflight); - } - - public static void GetJSOwnedObjectByGCHandleRef(int gcHandle, out object result) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - result = h.Target!; - } - - public static IntPtr GetJSOwnedObjectGCHandleRef(in object obj) - { - return JSProxyContext.MainThreadContext.GetJSOwnedObjectGCHandle(obj, GCHandleType.Normal); - } - - public static IntPtr CreateTaskSource() - { - var tcs = new TaskCompletionSource(); - return GetJSOwnedObjectGCHandleRef(tcs); - } - - public static void SetTaskSourceResultRef(int tcsGCHandle, in object result) - { - GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle; - // this is JS owned Normal handle. We always have a Target - TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; - tcs.SetResult(result); - } - - public static void SetTaskSourceFailure(int tcsGCHandle, string reason) - { - GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle; - // this is JS owned Normal handle. We always have a Target - TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; - tcs.SetException(new JSException(reason)); - } - - public static void GetTaskSourceTaskRef(int tcsGCHandle, out object result) - { - GCHandle handle = (GCHandle)(IntPtr)tcsGCHandle; - // this is JS owned Normal handle. We always have a Target - TaskCompletionSource tcs = (TaskCompletionSource)handle.Target!; - result = tcs.Task; - } - - public static void SetupJSContinuationRef(in Task _task, JSObject continuationObj) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - // HACK: Attempting to use the in-param will produce CS1628, so we make a temporary copy - // on the stack that can be captured by our local functions below - var task = _task; - - if (task.IsCompleted) - Complete(); - else - task.GetAwaiter().OnCompleted(Complete); - - void Complete() - { - try - { - if (task.Exception == null) - { - object? result; - Type task_type = task.GetType(); - if (task_type == typeof(Task)) - { - result = System.Array.Empty(); - } - else - { - result = JSHostImplementation.GetTaskResultMethodInfo(task_type)?.Invoke(task, null); - } - - continuationObj.Invoke("resolve", result); - } - else - { - continuationObj.Invoke("reject", task.Exception.ToString()); - } - } - catch (Exception e) - { - continuationObj.Invoke("reject", e.ToString()); - } - finally - { - continuationObj.Dispose(); - } - } - } - - public static string ObjectToStringRef(ref object o) - { - return o.ToString() ?? string.Empty; - } - - public static double GetDateValueRef(ref object dtv) - { - ArgumentNullException.ThrowIfNull(dtv); - - if (!(dtv is DateTime dt)) - throw new InvalidCastException(SR.Format(SR.UnableCastObjectToType, dtv.GetType(), typeof(DateTime))); - if (dt.Kind == DateTimeKind.Local) - dt = dt.ToUniversalTime(); - else if (dt.Kind == DateTimeKind.Unspecified) - dt = new DateTime(dt.Ticks, DateTimeKind.Utc); - return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); - } - - // HACK: We need to implicitly box by using an 'object' out-param. - // Note that the return value would have been boxed on the C#->JS transition anyway. - public static void CreateDateTimeRef(double ticks, out object result) - { - DateTimeOffset unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); - result = unixTime.DateTime; - } - - // we do this via reflection to allow trimming tools to trim dependency on Uri class and it's assembly - // if the user code has methods with Uri signature, they probably also have the Uri constructor - // if they don't have it, they could configure ILLing to protect it after they enabled trimming - // We believe that this code path is probably not even used in the wild - // System.Private.Uri is ~80KB large assembly so it's worth trimming - private static Type? uriType; - - [Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2077", Justification = "Done on purpose, see comment above.")] - [Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "Done on purpose, see comment above.")] - public static void CreateUriRef(string uri, out object? result) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - if (uriType == null) - { - // StringBuilder to confuse ILLink, which is too smart otherwise - StringBuilder sb = new StringBuilder("System.Uri, System.Private.Uri"); - uriType = Type.GetType(sb.ToString()); - } - // See: https://devblogs.microsoft.com/dotnet/customizing-trimming-in-net-core-5/ - if (uriType == null) throw new InvalidOperationException(SR.UriTypeMissing); - try - { - result = Activator.CreateInstance(uriType, uri); - } - catch (MissingMethodException ex) - { - throw new MissingMethodException(SR.UriConstructorMissing, ex); - } - } - - public static bool IsSimpleArrayRef(ref object a) - { - return a is System.Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0; - } - - public static string GetCallSignatureRef(IntPtr _methodHandle, in object objForRuntimeType) - { - var methodHandle = JSHostImplementation.GetMethodHandleFromIntPtr(_methodHandle); - - MethodBase? mb = objForRuntimeType is null ? MethodBase.GetMethodFromHandle(methodHandle) : MethodBase.GetMethodFromHandle(methodHandle, Type.GetTypeHandle(objForRuntimeType)); - if (mb is null) - return string.Empty; - - ParameterInfo[] parms = mb.GetParameters(); - int parmsLength = parms.Length; - if (parmsLength == 0) - return string.Empty; - - var result = new char[parmsLength]; - for (int i = 0; i < parmsLength; i++) - { - Type t = parms[i].ParameterType; - var mt = LegacyHostImplementation.GetMarshalTypeFromType(t); - result[i] = LegacyHostImplementation.GetCallSignatureCharacterForMarshalType(mt, null); - } - - return new string(result); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index 72cd8f87700e33..08619d5325df44 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -170,8 +170,6 @@ public static void InvokeJS(JSFunctionBinding signature, SpanThe method is executed on an architecture other than WebAssembly. // JavaScriptExports need to be protected from trimming because they are used from C/JS code which IL linker can't see [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.JavaScriptExports", "System.Runtime.InteropServices.JavaScript")] - // Same for legacy, but the type could be explicitly trimmed by setting WasmEnableLegacyJsInterop=false which would use ILLink.Descriptors.LegacyJsInterop.xml - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "System.Runtime.InteropServices.JavaScript.LegacyExportsTrimmingRoot", "System.Runtime.InteropServices.JavaScript")] public static JSFunctionBinding BindJSFunction(string functionName, string moduleName, ReadOnlySpan signatures) { if (RuntimeInformation.OSArchitecture != Architecture.Wasm) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs index 6be3f93d36563b..a3d1503af177d3 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.References.cs @@ -25,10 +25,6 @@ public SynchronizationContext SynchronizationContext } } -#if !DISABLE_LEGACY_JS_INTEROP - internal GCHandle? InFlight; - internal int InFlightCounter; -#endif internal bool _isDisposed; internal JSObject(IntPtr jsHandle, JSProxyContext ctx) @@ -37,41 +33,6 @@ internal JSObject(IntPtr jsHandle, JSProxyContext ctx) JSHandle = jsHandle; } -#if !DISABLE_LEGACY_JS_INTEROP - internal void AddInFlight() - { - AssertNotDisposed(); - lock (ProxyContext) - { - InFlightCounter++; - if (InFlightCounter == 1) - { - Debug.Assert(InFlight == null, "InFlight == null"); - InFlight = GCHandle.Alloc(this, GCHandleType.Normal); - } - } - } - - // Note that we could not use SafeHandle.DangerousAddRef() and DangerousRelease() - // because we could get to zero InFlightCounter multiple times across lifetime of the JSObject - // we only want JSObject to be disposed (from GC finalizer) once there is no in-flight reference and also no natural C# reference - internal void ReleaseInFlight() - { - lock (ProxyContext) - { - Debug.Assert(InFlightCounter != 0, "InFlightCounter != 0"); - - InFlightCounter--; - if (InFlightCounter == 0) - { - Debug.Assert(InFlight.HasValue, "InFlight.HasValue"); - InFlight.Value.Free(); - InFlight = null; - } - } - } -#endif - /// public override bool Equals([NotNullWhen(true)] object? obj) => obj is JSObject other && JSHandle == other.JSHandle; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs index dcb99ed1d3d4ec..5dfabdfee916bd 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs @@ -469,69 +469,6 @@ public static void ReleaseCSOwnedObject(JSObject proxy, bool skipJS) #endregion - #region Legacy - - // legacy - public void RegisterCSOwnedObject(JSObject proxy) - { - lock (this) - { - ThreadCsOwnedObjects[(int)proxy.JSHandle] = new WeakReference(proxy, trackResurrection: true); - } - } - - // legacy - public JSObject? GetCSOwnedObjectByJSHandle(nint jsHandle, int shouldAddInflight) - { - lock (this) - { - if (ThreadCsOwnedObjects.TryGetValue(jsHandle, out WeakReference? reference)) - { - reference.TryGetTarget(out JSObject? jsObject); - if (shouldAddInflight != 0) - { - jsObject?.AddInFlight(); - } - return jsObject; - } - } - return null; - } - - // legacy - public JSObject CreateCSOwnedProxy(nint jsHandle, LegacyHostImplementation.MappedType mappedType, int shouldAddInflight) - { - lock (this) - { - JSObject? res = null; - if (!ThreadCsOwnedObjects.TryGetValue(jsHandle, out WeakReference? reference) || - !reference.TryGetTarget(out res) || - res.IsDisposed) - { -#pragma warning disable CS0612 // Type or member is obsolete - res = mappedType switch - { - LegacyHostImplementation.MappedType.JSObject => new JSObject(jsHandle, JSProxyContext.MainThreadContext), - LegacyHostImplementation.MappedType.Array => new Array(jsHandle), - LegacyHostImplementation.MappedType.ArrayBuffer => new ArrayBuffer(jsHandle), - LegacyHostImplementation.MappedType.DataView => new DataView(jsHandle), - LegacyHostImplementation.MappedType.Function => new Function(jsHandle), - LegacyHostImplementation.MappedType.Uint8Array => new Uint8Array(jsHandle), - _ => throw new ArgumentOutOfRangeException(nameof(mappedType)) - }; -#pragma warning restore CS0612 // Type or member is obsolete - ThreadCsOwnedObjects[jsHandle] = new WeakReference(res, trackResurrection: true); - } - if (shouldAddInflight != 0) - { - res.AddInFlight(); - } - return res; - } - } - - #endregion - #region Dispose private void Dispose(bool disposing) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs deleted file mode 100644 index 7ce50a8e741f28..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Array.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Runtime.InteropServices.JavaScript -{ - /// - /// Initializes a new instance of JavaScript Core Array class. - /// - [Obsolete] - public class Array : JSObject - { - /// - /// Initializes a new instance of the Array class. - /// - /// Parameters. - public Array(params object[] _params) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(Array), _params), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - /// - /// Initializes a new instance of the Array/> class. - /// - /// Js handle. - internal Array(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext) - { } - - /// - /// Push the specified elements. - /// - /// The new length of the Array push was called on. - /// Elements. - public int Push(params object[] elements) => (int)this.Invoke("push", elements); - - /// - /// Pop this instance. - /// - /// The element removed from the array or null if the array was empty. - public object Pop() => (object)this.Invoke("pop"); - - /// - /// Remove the first element of the Array and return that element - /// - /// The removed element. - public object Shift() => this.Invoke("shift"); - - /// - /// Add to the array starting at index 0 - /// - /// The length after shift. - /// Elements. - public int UnShift(params object[] elements) => (int)this.Invoke("unshift", elements); - - /// - /// Index of the search element. - /// - /// The index of first occurrence of searchElement in the Array or -1 if not Found. - /// Search element. - /// The index to start the search from. - public int IndexOf(object searchElement, int fromIndex = 0) => (int)this.Invoke("indexOf", searchElement, fromIndex); - - /// - /// Finds the index of the last occurrence of - /// - /// The index of the last occurrence. - /// Search element. - public int LastIndexOf(object searchElement) => (int)this.Invoke("lastIndexOf", searchElement); - - /// - /// Finds the index of the last occurrence of between 0 and . - /// - /// The index of the last occurrence. - /// Search element. - /// End index. - public int LastIndexOf(object searchElement, int endIndex) => (int)this.Invoke("lastIndexOf", searchElement, endIndex); - - /// - /// Gets or sets the Array with the index specified by . - /// - /// The index. - public object this[int i] - { - get - { - this.AssertNotDisposed(); - - Interop.Runtime.GetByIndexRef(JSHandle, i, out int exception, out object indexValue); - - if (exception != 0) - throw new JSException((string)indexValue); - LegacyHostImplementation.ReleaseInFlight(indexValue); - return indexValue; - } - set - { - this.AssertNotDisposed(); - - Interop.Runtime.SetByIndexRef(JSHandle, i, value, out int exception, out object res); - - if (exception != 0) - throw new JSException((string)res); - - } - } - - /// - /// Gets or sets the length. - /// - /// The length. - public int Length - { - get => Convert.ToInt32(this.GetObjectProperty("length")); - set => this.SetObjectProperty("length", value, false); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs deleted file mode 100644 index 92e78a14fe1b27..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/ArrayBuffer.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.InteropServices.JavaScript -{ - [Obsolete] - public class ArrayBuffer : JSObject - { - /// - /// Initializes a new instance of the JavaScript Core ArrayBuffer class. - /// - /// Length. - public ArrayBuffer(int length) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(ArrayBuffer), new object[] { length }), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - /// - /// Initializes a new instance of the JavaScript Core ArrayBuffer class. - /// - /// Js handle. - internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext) - { } - - /// - /// The length of an ArrayBuffer in bytes. - /// - /// The length of the underlying ArrayBuffer in bytes. - public int ByteLength => (int)this.GetObjectProperty("byteLength"); - - /// - /// Gets a value indicating whether this ArrayBuffer is view. - /// - /// if is view; otherwise, . - public bool IsView => (bool)this.GetObjectProperty("isView"); - - /// - /// Slice the specified begin. - /// - /// The slice. - /// Begin. - public ArrayBuffer Slice(int begin) => (ArrayBuffer)this.Invoke("slice", begin); - - /// - /// Slice the specified begin and end. - /// - /// The slice. - /// Begin. - /// End. - public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)this.Invoke("slice", begin, end); - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs deleted file mode 100644 index 1046fa8f99fa45..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/DataView.cs +++ /dev/null @@ -1,233 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.InteropServices.JavaScript -{ - /// - /// The DataView view provides a low-level interface for reading and writing multiple number types in a - /// binary ArrayBuffer, without having to care about the platform's endianness. - /// - [Obsolete] - public class DataView : JSObject - { - /// - /// Initializes a new instance of the DataView class. - /// - /// ArrayBuffer to use as the storage backing the new DataView object. - public DataView(ArrayBuffer buffer) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer }), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - /// - /// Initializes a new instance of the DataView class. - /// - /// ArrayBuffer to use as the storage backing the new DataView object. - /// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte. - public DataView(ArrayBuffer buffer, int byteOffset) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset }), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - /// - /// Initializes a new instance of the DataView class. - /// - /// ArrayBuffer to use as the storage backing the new DataView object. - /// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte. - /// The number of elements in the byte array. If unspecified, the view's length will match the buffer's length. - public DataView(ArrayBuffer buffer, int byteOffset, int byteLength) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(DataView), new object[] { buffer, byteOffset, byteLength }), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - /// - /// Initializes a new instance of the DataView class. - /// - /// Js handle. - internal DataView(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext) - { } - - /// - /// Gets the length (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only. - /// - /// The length (in bytes) of this view. - public int ByteLength => (int)this.GetObjectProperty("byteLength"); - - /// - /// Gets the offset (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only. - /// - /// The offset (in bytes) of this view. - public int ByteOffset => (int)this.GetObjectProperty("byteOffset"); - - /// - /// Gets the ArrayBuffer referenced by this view. Fixed at construction time and thus read only. - /// - /// The ArrayBuffer. - public ArrayBuffer Buffer => (ArrayBuffer)this.GetObjectProperty("buffer"); - - /// - /// Gets the signed 32-bit float (float) at the specified byte offset from the start of the DataView. - /// - /// A signed 32-bit float number. - /// Byte offset. - /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - public float GetFloat32(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getFloat32", byteOffset, littleEndian)); - - /// - /// Gets the signed 64-bit double (double) at the specified byte offset from the start of the DataView. - /// - /// A signed 64-bit coulbe number. - /// Byte offset. - /// Indicates whether the 64-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - public double GetFloat64(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getFloat64", byteOffset, littleEndian)); - - /// - /// Gets the signed 16-bit integer (short) at the specified byte offset from the start of the DataView. - /// - /// A signed 16-bit ineger (short) number. - /// Byte offset. - /// Indicates whether the 16-bit integer (short) is stored in little- or big-endian format. If false, a big-endian value is read. - public short GetInt16(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getInt16", byteOffset, littleEndian)); - - /// - /// Gets the signed 32-bit integer (int) at the specified byte offset from the start of the DataView. - /// - /// A signed 32-bit ineger (int) number. - /// Byte offset. - /// Indicates whether the 32-bit integer (int) is stored in little- or big-endian format. If false, a big-endian value is read. - public int GetInt32(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getInt32", byteOffset, littleEndian)); - - /// - /// Gets the signed 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. - /// - /// A signed 8-bit byte (sbyte) number. - /// Byte offset. - /// Indicates whether the 8-bit byte is stored in little- or big-endian format. If false, a big-endian value is read. - [CLSCompliant(false)] - public sbyte GetInt8(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getInt8", byteOffset, littleEndian)); - - /// - /// Gets the unsigned 16-bit integer (short) at the specified byte offset from the start of the DataView. - /// - /// A unsigned 16-bit integer (ushort) number. - /// Byte offset. - /// Indicates whether the unsigned 16-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - [CLSCompliant(false)] - public ushort GetUint16(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getUint16", byteOffset, littleEndian)); - - /// - /// Gets the usigned 32-bit integer (uint) at the specified byte offset from the start of the DataView. - /// - /// A usigned 32-bit ineger (uint) number. - /// Byte offset. - /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - [CLSCompliant(false)] - public uint GetUint32(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getUint32", byteOffset, littleEndian)); - - /// - /// Gets the unsigned 8-bit byte (byte) at the specified byte offset from the start of the DataView. - /// - /// A unsigned 8-bit byte (byte) number. - /// Byte offset. - /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - public byte GetUint8(int byteOffset, bool littleEndian = false) => UnBoxValue(this.Invoke("getUint8", byteOffset, littleEndian)); - - /// - /// Sets the signed 32-bit float (float) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// float value. - /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - public void SetFloat32(int byteOffset, float value, bool littleEndian = false) => this.Invoke("setFloat32", byteOffset, value, littleEndian); - - /// - /// Sets the signed 64-bit double (double) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// double value. - /// Indicates whether the 64-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - public void SetFloat64(int byteOffset, double value, bool littleEndian = false) => this.Invoke("setFloat64", byteOffset, value, littleEndian); - - /// - /// Sets the signed 16-bit integer (short) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// short value. - /// Indicates whether the 16-bit integer (short) is stored in little- or big-endian format. If false, a big-endian value is read. - public void SetInt16(int byteOffset, short value, bool littleEndian = false) => this.Invoke("setInt16", byteOffset, value, littleEndian); - - /// - /// Sets the signed 32-bit integer (int) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// int value. - /// Indicates whether the 32-bit integer (int) is stored in little- or big-endian format. If false, a big-endian value is read. - public void SetInt32(int byteOffset, int value, bool littleEndian = false) => this.Invoke("setInt32", byteOffset, value, littleEndian); - - /// - /// Sets the signed 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// sbyte value. - /// Indicates whether the 8-bit byte is stored in little- or big-endian format. If false, a big-endian value is read. - [CLSCompliant(false)] - public void SetInt8(int byteOffset, sbyte value, bool littleEndian = false) => this.Invoke("setInt8", byteOffset, value, littleEndian); - - /// - /// Sets the unsigned 16-bit integer (short) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// ushort value. - /// Indicates whether the unsigned 16-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - [CLSCompliant(false)] - public void SetUint16(int byteOffset, ushort value, bool littleEndian = false) => this.Invoke("setUint16", byteOffset, value, littleEndian); - - /// - /// Sets the usigned 32-bit integer (uint) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// uint value. - /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - [CLSCompliant(false)] - public void SetUint32(int byteOffset, uint value, bool littleEndian = false) => this.Invoke("setUint32", byteOffset, value, littleEndian); - - /// - /// Sets the unsigned 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. - /// - /// Byte offset. - /// byte value. - /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. - public void SetUint8(int byteOffset, byte value, bool littleEndian = false) => this.Invoke("setUint8", byteOffset, value, littleEndian); - - private static U UnBoxValue(object jsValue) where U : struct - { - if (jsValue == null) - { - throw new InvalidCastException(SR.Format(SR.UnableCastNullToType, typeof(U))); - } - - var type = jsValue.GetType(); - if (type.IsPrimitive) - { - return (U)Convert.ChangeType(jsValue, typeof(U)); - } - else - { - throw new InvalidCastException(SR.Format(SR.UnableCastObjectToType, type, typeof(U))); - } - } - - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs deleted file mode 100644 index ebce83d01417ad..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Function.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime.InteropServices.JavaScript -{ - /// - /// The Function constructor creates a new Function object. - /// - /// - /// Calling the constructor directly can create functions dynamically, but suffers from security and similar - /// (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor - /// allows executing code in the global scope, prompting better programming habits and allowing for more efficient - /// code minification. - /// - [Obsolete] - public class Function : JSObject - { - public Function(params object[] args) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(Function), args), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - internal Function(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext) - { } - - /// - /// The Apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object). - /// - /// The apply. - /// This argument. - /// Arguments. - public object Apply(object? thisArg, object[]? argsArray = null) => this.Invoke("apply", thisArg, argsArray); - - /// - /// Creates a new Function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. - /// - /// The bind. - /// This argument. - /// Arguments. - public Function Bind(object? thisArg, object[]? argsArray = null) => (Function)this.Invoke("bind", thisArg, argsArray); - - /// - /// Calls a function with a given `this` value and arguments provided individually. - /// - /// The result of calling the function with the specified `this` value and arguments. - /// Optional (null value). The value of this provided for the call to a function. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects. - /// Optional. Arguments for the function. - public object Call(object? thisArg, params object[] argsArray) - { - object?[] argsList = new object[argsArray.Length + 1]; - argsList[0] = thisArg; - System.Array.Copy(argsArray, 0, argsList, 1, argsArray.Length); - return this.Invoke("call", argsList); - } - - /// - /// Calls a function with a null `this` value. - /// - /// The result of calling the function. - public object Call() => Call(null); - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs deleted file mode 100644 index c0503f449fd410..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/LegacyHostImplementation.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; - -namespace System.Runtime.InteropServices.JavaScript -{ - [SupportedOSPlatform("browser")] - internal static class LegacyHostImplementation - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ReleaseInFlight(object obj) - { - JSObject? jsObj = obj as JSObject; - jsObj?.ReleaseInFlight(); - } - - public static MarshalType GetMarshalTypeFromType(Type type) - { - if (type is null) - return MarshalType.VOID; - - var typeCode = Type.GetTypeCode(type); - if (type.IsEnum) - { - switch (typeCode) - { - case TypeCode.Int32: - case TypeCode.UInt32: - return MarshalType.ENUM; - case TypeCode.Int64: - case TypeCode.UInt64: - return MarshalType.ENUM64; - default: - throw new ArgumentException(SR.Format(SR.UnsupportedEnumType, type.FullName), nameof(type)); - } - } - - switch (typeCode) - { - case TypeCode.SByte: - case TypeCode.Int16: - case TypeCode.Int32: - return MarshalType.INT; - case TypeCode.Byte: - case TypeCode.UInt16: - case TypeCode.UInt32: - return MarshalType.UINT32; - case TypeCode.Boolean: - return MarshalType.BOOL; - case TypeCode.Int64: - return MarshalType.INT64; - case TypeCode.UInt64: - return MarshalType.UINT64; - case TypeCode.Single: - return MarshalType.FP32; - case TypeCode.Double: - return MarshalType.FP64; - case TypeCode.String: - return MarshalType.STRING; - case TypeCode.Char: - return MarshalType.CHAR; - } - - if (type.IsArray) - { - if (!type.IsSZArray) - throw new ArgumentException(SR.Format(SR.UnsupportedArrayType, type.FullName), nameof(type)); - - var elementType = type.GetElementType(); - switch (Type.GetTypeCode(elementType)) - { - case TypeCode.Byte: - return MarshalType.ARRAY_UBYTE; - case TypeCode.SByte: - return MarshalType.ARRAY_BYTE; - case TypeCode.Int16: - return MarshalType.ARRAY_SHORT; - case TypeCode.UInt16: - return MarshalType.ARRAY_USHORT; - case TypeCode.Int32: - return MarshalType.ARRAY_INT; - case TypeCode.UInt32: - return MarshalType.ARRAY_UINT; - case TypeCode.Single: - return MarshalType.ARRAY_FLOAT; - case TypeCode.Double: - return MarshalType.ARRAY_DOUBLE; - default: - throw new ArgumentException(SR.Format(SR.UnsupportedElementType, elementType), nameof(type)); - } - } - else if (type == typeof(IntPtr)) - return MarshalType.POINTER; - else if (type == typeof(UIntPtr)) - return MarshalType.POINTER; - else if (type == typeof(SafeHandle)) - return MarshalType.SAFEHANDLE; - else if (typeof(Delegate).IsAssignableFrom(type)) - return MarshalType.DELEGATE; - else if ((type == typeof(Task)) || typeof(Task).IsAssignableFrom(type)) - return MarshalType.TASK; - else if (type.FullName == "System.Uri") - return MarshalType.URI; - else if (type.IsPointer) - return MarshalType.POINTER; - - if (type.IsValueType) - return MarshalType.VT; - else - return MarshalType.OBJECT; - } - - public static char GetCallSignatureCharacterForMarshalType(MarshalType type, char? defaultValue) - { - switch (type) - { - case MarshalType.BOOL: - return 'b'; - case MarshalType.UINT32: - case MarshalType.POINTER: - return 'I'; - case MarshalType.INT: - return 'i'; - case MarshalType.UINT64: - return 'L'; - case MarshalType.INT64: - return 'l'; - case MarshalType.FP32: - return 'f'; - case MarshalType.FP64: - return 'd'; - case MarshalType.STRING: - return 's'; - case MarshalType.URI: - return 'u'; - case MarshalType.SAFEHANDLE: - return 'h'; - case MarshalType.ENUM: - return 'j'; // this is wrong for uint enums - case MarshalType.ENUM64: - return 'k'; // this is wrong for ulong enums - case MarshalType.TASK: - case MarshalType.DELEGATE: - case MarshalType.OBJECT: - return 'o'; - case MarshalType.VT: - return 'a'; - default: - if (defaultValue.HasValue) - return defaultValue.Value; - else - throw new ArgumentException(SR.Format(SR.UnsupportedLegacyMarshlerType, type), nameof(type)); - } - } - - // see src/mono/wasm/driver.c MARSHAL_TYPE_xxx - public enum MarshalType : int - { - NULL = 0, - INT = 1, - FP64 = 2, - STRING = 3, - VT = 4, - DELEGATE = 5, - TASK = 6, - OBJECT = 7, - BOOL = 8, - ENUM = 9, - URI = 22, - SAFEHANDLE = 23, - ARRAY_BYTE = 10, - ARRAY_UBYTE = 11, - ARRAY_UBYTE_C = 12, - ARRAY_SHORT = 13, - ARRAY_USHORT = 14, - ARRAY_INT = 15, - ARRAY_UINT = 16, - ARRAY_FLOAT = 17, - ARRAY_DOUBLE = 18, - FP32 = 24, - UINT32 = 25, - INT64 = 26, - UINT64 = 27, - CHAR = 28, - STRING_INTERNED = 29, - VOID = 30, - ENUM64 = 31, - POINTER = 32 - } - - // see src/mono/wasm/driver.c MARSHAL_ERROR_xxx - public enum MarshalError : int - { - BUFFER_TOO_SMALL = 512, - NULL_CLASS_POINTER = 513, - NULL_TYPE_POINTER = 514, - UNSUPPORTED_TYPE = 515, - FIRST = BUFFER_TOO_SMALL - } - - // please keep BINDING wasm_type_symbol in sync - public enum MappedType - { - JSObject = 0, - Array = 1, - ArrayBuffer = 2, - DataView = 3, - Function = 4, - Uint8Array = 11, - } - -#if FEATURE_WASM_THREADS - public static void ThrowIfLegacyWorkerThread() - { - if (Environment.CurrentManagedThreadId != 1) - { - throw new PlatformNotSupportedException("Legacy interop is not supported with WebAssembly threads."); - } - } -#endif - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs deleted file mode 100644 index 81535a162d51ca..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Runtime.cs +++ /dev/null @@ -1,117 +0,0 @@ -// 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.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace System.Runtime.InteropServices.JavaScript -{ - [Obsolete] - public static class Runtime - { - public static object GetGlobalObject(string str) - => JavaScriptImports.GetGlobalObject(str); - - /// - /// Invoke a named method of the object, or throws a JSException on error. - /// - /// thisArg - /// The name of the method to invoke. - /// The argument list to pass to the invoke command. - /// - /// - /// The return value can either be a primitive (string, int, double), a JSObject for JavaScript objects, a - /// System.Threading.Tasks.Task(object) for JavaScript promises, an array of - /// a byte, int or double (for Javascript objects typed as ArrayBuffer) or a - /// System.Func to represent JavaScript functions. The specific version of - /// the Func that will be returned depends on the parameters of the Javascript function - /// and return value. - /// - /// - /// The value of a returned promise (The Task(object) return) can in turn be any of the above - /// valuews. - /// - /// - public static object Invoke(this JSObject self, string method, params object?[] args) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - ArgumentNullException.ThrowIfNull(self); - self.AssertNotDisposed(); - Interop.Runtime.InvokeJSWithArgsRef(self.JSHandle, method, args, out int exception, out object res); - if (exception != 0) - throw new JSException((string)res); - LegacyHostImplementation.ReleaseInFlight(res); - return res; - } - - /// - /// Returns the named property from the object, or throws a JSException on error. - /// - /// thisArg - /// The name of the property to lookup. - /// - /// This method can raise a JSException if fetching the property in Javascript raises an exception. - /// - /// - /// - /// The return value can either be a primitive (string, int, double), a - /// JSObject for JavaScript objects, a - /// System.Threading.Tasks.Task (object) for JavaScript promises, an array of - /// a byte, int or double (for Javascript objects typed as ArrayBuffer) or a - /// System.Func to represent JavaScript functions. The specific version of - /// the Func that will be returned depends on the parameters of the Javascript function - /// and return value. - /// - /// - /// The value of a returned promise (The Task(object) return) can in turn be any of the above - /// valuews. - /// - /// - public static object GetObjectProperty(this JSObject self, string name) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - ArgumentNullException.ThrowIfNull(self); - self.AssertNotDisposed(); - - Interop.Runtime.GetObjectPropertyRef(self.JSHandle, name, out int exception, out object propertyValue); - if (exception != 0) - throw new JSException((string)propertyValue); - LegacyHostImplementation.ReleaseInFlight(propertyValue); - return propertyValue; - } - - /// - /// Sets the named property to the provided value. - /// - /// - /// - /// thisArg - /// The name of the property to lookup. - /// The value can be a primitive type (int, double, string, bool), an - /// array that will be surfaced as a typed ArrayBuffer (byte[], sbyte[], short[], ushort[], - /// float[], double[]) - /// Defaults to and creates the property on the javascript object if not found, if set to it will not create the property if it does not exist. If the property exists, the value is updated with the provided value. - /// - public static void SetObjectProperty(this JSObject self, string name, object? value, bool createIfNotExists = true, bool hasOwnProperty = false) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - ArgumentNullException.ThrowIfNull(self); - self.AssertNotDisposed(); - - Interop.Runtime.SetObjectPropertyRef(self.JSHandle, name, in value, createIfNotExists, hasOwnProperty, out int exception, out object res); - if (exception != 0) - throw new JSException(SR.Format(SR.ErrorLegacySettingProperty, name, self.JSHandle, res)); - } - - public static void AssertInFlight(this JSObject self, int expectedInFlightCount) - { - if (self.InFlightCounter != expectedInFlightCount) throw new InvalidOperationException(SR.Format(SR.UnsupportedLegacyMarshlerType, self.JSHandle, expectedInFlightCount, self.InFlightCounter)); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs deleted file mode 100644 index 1cea722c1cc623..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Legacy/Uint8Array.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Runtime.InteropServices.JavaScript -{ - [Obsolete] - public sealed class Uint8Array : JSObject - { - public Uint8Array(int length) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { length }), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - public Uint8Array(ArrayBuffer buffer) - : base(JavaScriptImports.CreateCSOwnedObject(nameof(Uint8Array), new object[] { buffer }), JSProxyContext.MainThreadContext) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - JSProxyContext.MainThreadContext.RegisterCSOwnedObject(this); - } - - internal Uint8Array(IntPtr jsHandle) : base(jsHandle, JSProxyContext.MainThreadContext) - { } - - public int Length - { - get => Convert.ToInt32(this.GetObjectProperty("length")); - set => this.SetObjectProperty("length", value, false); - } - - /// - /// Defines an implicit conversion of JavaScript Core Uint8Array class to a Span<byte> - /// - public static implicit operator Span(Uint8Array typedarray) => typedarray.ToArray(); - - public static implicit operator Uint8Array(Span span) => From(span); - - public byte[] ToArray() - { - this.AssertNotDisposed(); - - Interop.Runtime.TypedArrayToArrayRef(JSHandle, out int exception, out object res); - - if (exception != 0) - throw new JSException((string)res); - return (byte[])res; - } - - public static unsafe Uint8Array From(ReadOnlySpan span) - { -#if FEATURE_WASM_THREADS - LegacyHostImplementation.ThrowIfLegacyWorkerThread(); -#endif - // source has to be instantiated. - if (span == null) - { - throw new System.ArgumentException(SR.Format(SR.ArgumentCannotBeNull, nameof(span)), nameof(span)); - } - - ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); - fixed (byte* ptr = bytes) - { - Interop.Runtime.TypedArrayFromRef((int)ptr, 0, span.Length, sizeof(byte), (int)TypedArrayTypeCode.Uint8Array, out int exception, out object res); - if (exception != 0) - throw new JSException((string)res); - var r = (Uint8Array)res; - r.ReleaseInFlight(); - return r; - } - } - - public enum TypedArrayTypeCode - { - Uint8Array = 6, - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj deleted file mode 100644 index a46f95dde5a921..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System.Runtime.InteropServices.JavaScript.Legacy.Tests.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - true - $(NetCoreAppCurrent)-browser - true - $(WasmXHarnessArgs) --engine-arg=--expose-gc --web-server-use-cop - 0612 - true - $(DefineConstants);DISABLE_LEGACY_JS_INTEROP - WasmTestOnBrowser - - <_XUnitBackgroundExec>false - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ArrayTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ArrayTests.cs deleted file mode 100644 index ae247a1bb2f63c..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ArrayTests.cs +++ /dev/null @@ -1,173 +0,0 @@ -// 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 System.Runtime.InteropServices.JavaScript.Tests -{ - public static class ArrayTests - { - [Fact] - public static void ArrayLength() - { - Array d = new Array(); - Assert.Equal(0, d.Length); - } - - [Fact] - public static void ArrayLength1() - { - Array d = new Array(50); - Assert.Equal(50, d.Length); - } - - [Fact] - public static void Array_GetSetItem() - { - var jsArray = new Array(7, 8, 9, 10, 11, 12, 13); - IList iList = new int[] { 7, 8, 9, 10, 11, 12, 13 }; - - Assert.Equal(jsArray.Length, iList.Count); - for (int i = 0; i < iList.Count; i++) - { - Assert.Equal(jsArray[i], iList[i]); - - iList[i] = 99; - jsArray[i] = 99; - Assert.Equal(99, iList[i]); - Assert.Equal(99, jsArray[i]); - } - } - - [Fact] - public static void Array_GetSetItemInvalid() - { - var jsArray = new Array(7, 8, 9, 10, 11, 12, 13); - Assert.Null(jsArray[-1]); - Assert.Null(jsArray[jsArray.Length]); - Assert.Equal(0, jsArray[-1] = 0); - Assert.Equal(0, jsArray[jsArray.Length] = 0); - } - - [Fact] - public static void Array_GetItemIndex() - { - var jsArray = new Array(7, 8, 9, 10, 11, 12, 13); - Assert.Equal(7, jsArray[0]); - Assert.Equal(8, jsArray[1]); - Assert.Equal(9, jsArray[2]); - Assert.Equal(10, jsArray[3]); - Assert.Equal(11, jsArray[4]); - Assert.Equal(12, jsArray[5]); - Assert.Equal(13, jsArray[6]); - } - - [Fact] - public static void Array_GetSetItemIndex() - { - var jsArray = new Array(7, 8, 9, 10, 11, 12, 13); - for (int d = 0; d < jsArray.Length; d++) - { - jsArray[d] = (int)jsArray[d] + 1; - } - Assert.Equal(8, jsArray[0]); - Assert.Equal(9, jsArray[1]); - Assert.Equal(10, jsArray[2]); - Assert.Equal(11, jsArray[3]); - Assert.Equal(12, jsArray[4]); - Assert.Equal(13, jsArray[5]); - Assert.Equal(14, jsArray[6]); - } - - [Fact] - public static void Array_Pop() - { - var jsArray = new Array(7, 8, 9, 10, 11, 12, 13); - Assert.Equal(13, jsArray.Pop()); - Assert.Equal(12, jsArray.Pop()); - Assert.Equal(11, jsArray.Pop()); - Assert.Equal(10, jsArray.Pop()); - Assert.Equal(9, jsArray.Pop()); - Assert.Equal(8, jsArray.Pop()); - Assert.Equal(7, jsArray.Pop()); - Assert.Equal(0, jsArray.Length); - } - - [Fact] - public static void Array_PushPop() - { - var objArray = new object[] { "test7", "test8", "test9", "test10", "test11", "test12", "test13" }; - var jsArray = new Array(); - for (int d = 0; d < objArray.Length; d++) - { - jsArray.Push(objArray[d]); - } - Assert.Equal("test13", jsArray.Pop()); - Assert.Equal("test12", jsArray.Pop()); - Assert.Equal("test11", jsArray.Pop()); - Assert.Equal("test10", jsArray.Pop()); - Assert.Equal("test9", jsArray.Pop()); - Assert.Equal("test8", jsArray.Pop()); - Assert.Equal("test7", jsArray.Pop()); - Assert.Equal(0, jsArray.Length); - } - - [Fact] - public static void Array_PushShift() - { - var objArray = new object[] { "test7", "test8", "test9", "test10", "test11", "test12", "test13" }; - var jsArray = new Array(); - for (int d = 0; d < objArray.Length; d++) - { - jsArray.Push(objArray[d]); - } - Assert.Equal("test7", jsArray.Shift()); - Assert.Equal("test8", jsArray.Shift()); - Assert.Equal("test9", jsArray.Shift()); - Assert.Equal("test10", jsArray.Shift()); - Assert.Equal("test11", jsArray.Shift()); - Assert.Equal("test12", jsArray.Shift()); - Assert.Equal("test13", jsArray.Shift()); - Assert.Equal(0, jsArray.Length); - } - - [Fact] - public static void Array_UnShiftShift() - { - var objArray = new object[] { "test7", "test8", "test9", "test10", "test11", "test12", "test13" }; - var jsArray = new Array(); - for (int d = 0; d < objArray.Length; d++) - { - Assert.Equal(d + 1, jsArray.UnShift(objArray[d])); - } - Assert.Equal("test13", jsArray.Shift()); - Assert.Equal("test12", jsArray.Shift()); - Assert.Equal("test11", jsArray.Shift()); - Assert.Equal("test10", jsArray.Shift()); - Assert.Equal("test9", jsArray.Shift()); - Assert.Equal("test8", jsArray.Shift()); - Assert.Equal("test7", jsArray.Shift()); - Assert.Equal(0, jsArray.Length); - } - - [Fact] - public static void Array_IndexOf() - { - var beasts = new Array("ant", "bison", "camel", "duck", "bison"); - Assert.Equal(1, beasts.IndexOf("bison")); - Assert.Equal(4, beasts.IndexOf("bison", 2)); - Assert.Equal(-1, beasts.IndexOf("giraffe")); - } - - [Fact] - public static void Array_LastIndexOf() - { - var beasts = new Array("Dodo", "Tiger", "Penguin", "Dodo"); - Assert.Equal(3, beasts.LastIndexOf("Dodo")); - Assert.Equal(1, beasts.LastIndexOf("Tiger")); - Assert.Equal(0, beasts.LastIndexOf("Dodo", 2)); // The array is searched backwards - Assert.Equal(-1, beasts.LastIndexOf("giraffe")); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DataViewTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DataViewTests.cs deleted file mode 100644 index 4e06bf20f005a1..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DataViewTests.cs +++ /dev/null @@ -1,134 +0,0 @@ -// 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 System.Runtime.InteropServices.JavaScript.Tests -{ - public static class DataViewTests - { - [Fact] - public static void DataViewConstructor() - { - // create an ArrayBuffer with a size in bytes - var buffer = new ArrayBuffer(16); - - // Create a couple of views - var view1 = new DataView(buffer); - var view2 = new DataView(buffer, 12, 4); //from byte 12 for the next 4 bytes - view1.SetInt8(12, 42); // put 42 in slot 12 - - Assert.Equal(42, view2.GetInt8(0)); - } - - public static IEnumerable ArrayBuffer_Test_Data() - { - yield return new object[] { new ArrayBuffer(12) }; - } - - [Theory] - [MemberData(nameof(ArrayBuffer_Test_Data))] - public static void DataViewArrayBuffer(ArrayBuffer buffer) - { - var x = new DataView(buffer); - Assert.True(buffer == x.Buffer); - } - - [Theory] - [MemberData(nameof(ArrayBuffer_Test_Data))] - public static void DataViewByteLength(ArrayBuffer buffer) - { - var x = new DataView(buffer, 4, 2); - Assert.Equal(2, x.ByteLength); - } - - [Theory] - [MemberData(nameof(ArrayBuffer_Test_Data))] - public static void DataViewByteOffset(ArrayBuffer buffer) - { - var x = new DataView(buffer, 4, 2); - Assert.Equal(4, x.ByteOffset); - } - - public static IEnumerable DataView_Test_Data() - { - yield return new object[] { new DataView(new ArrayBuffer(12), 0) }; - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - - public static void DataViewGetFloat32(DataView view) - { - view.SetFloat32(1, (float)Math.PI); - Assert.Equal((float)Math.Round(Math.PI, 5), (float)Math.Round(view.GetFloat32(1), 5)); - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetFloat64(DataView view) - { - view.SetFloat64(1, (float)Math.PI); - Assert.Equal(Math.Round(Math.PI, 5), Math.Round(view.GetFloat64(1), 5)); - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetInt16(DataView view) - { - view.SetInt16(1, 1234); - Assert.Equal(1234, view.GetInt16(1)); - - view.SetInt16(1, -1234); - Assert.Equal(-1234, view.GetInt16(1)); - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetInt32(DataView view) - { - view.SetInt32(1, 1234); - Assert.Equal(1234, view.GetInt32(1)); - - view.SetInt32(1, -1234); - Assert.Equal(-1234, view.GetInt32(1)); - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetInt8(DataView view) - { - view.SetInt8(1, 123); - Assert.Equal(123, view.GetInt8(1)); - - view.SetInt8(1, -123); - Assert.Equal(-123, view.GetInt8(1)); - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetUint16(DataView view) - { - view.SetUint16(1, 1234); - Assert.Equal(1234, view.GetUint16(1)); - - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetUint32(DataView view) - { - view.SetUint32(1, 1234); - Assert.Equal(1234u, view.GetUint32(1)); - } - - [Theory] - [MemberData(nameof(DataView_Test_Data))] - public static void DataViewGetUint8(DataView view) - { - view.SetUint8(1, 123); - Assert.Equal(123u, view.GetUint8(1)); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs deleted file mode 100644 index f5a49b0ef4c4bc..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs +++ /dev/null @@ -1,449 +0,0 @@ -// 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.Threading.Tasks; -using Xunit; -using System.Runtime.InteropServices.JavaScript; - -namespace System.Runtime.InteropServices.JavaScript.Tests -{ - public static class DelegateTests - { - private static Function _objectPrototype; - - [Fact] - public static void InvokeFunction() - { - HelperMarshal._functionResultValue = 0; - HelperMarshal._i32Value = 0; - - Utils.InvokeJS(@" - var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [ ]); - var res = funcDelegate (10, 20); - App.call_test_method (""InvokeI32"", [ res, res ]); - "); - - Assert.Equal(30, HelperMarshal._functionResultValue); - Assert.Equal(60, HelperMarshal._i32Value); - } - - [Fact] - public static void InvokeFunctionInLoopUsingConstanceValues() - { - HelperMarshal._functionResultValue = 0; - HelperMarshal._i32Value = 0; - - Utils.InvokeJS(@" - var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [ ]); - var res = funcDelegate (10, 20); - for (let x = 0; x < 1000; x++) - { - res = funcDelegate (10, 20); - } - App.call_test_method (""InvokeI32"", [ res, res ]); - "); - - Assert.Equal(30, HelperMarshal._functionResultValue); - Assert.Equal(60, HelperMarshal._i32Value); - } - - [Fact] - public static void InvokeFunctionInLoopUsingIncrementedValues() - { - HelperMarshal._functionResultValue = 0; - HelperMarshal._i32Value = 0; - Utils.InvokeJS(@" - var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [ ]); - var res = funcDelegate (10, 20); - for (let x = 0; x < 1000; x++) - { - res = funcDelegate (x, x); - } - App.call_test_method (""InvokeI32"", [ res, res ]); - "); - - Assert.Equal(1998, HelperMarshal._functionResultValue); - Assert.Equal(3996, HelperMarshal._i32Value); - } - - [Fact] - public static void InvokeActionTReturnedByInvokingFuncT() - { - HelperMarshal._functionActionResultValue = 0; - HelperMarshal._functionActionResultValueOfAction = 0; - - Utils.InvokeJS(@" - var funcDelegate = App.call_test_method (""CreateFunctionDelegateWithAction"", [ ]); - var actionDelegate = funcDelegate (10, 20); - actionDelegate(30,40); - "); - - Assert.Equal(30, HelperMarshal._functionActionResultValue); - Assert.Equal(70, HelperMarshal._functionActionResultValueOfAction); - } - - [Fact] - public static void InvokeActionIntInt() - { - HelperMarshal._actionResultValue = 0; - - Utils.InvokeJS(@" - var actionDelegate = App.call_test_method (""CreateActionDelegate"", [ ]); - actionDelegate(30,40); - "); - - Assert.Equal(70, HelperMarshal._actionResultValue); - } - - [Fact] - public static void InvokeActionFloatIntToIntInt() - { - HelperMarshal._actionResultValue = 0; - var ex = Assert.Throws(()=>Utils.InvokeJS(@" - var actionDelegate = App.call_test_method (""CreateActionDelegate"", [ ]); - actionDelegate(3.14,40); - ")); - - Assert.Contains("Value is not an integer: 3.14 (number)", ex.Message); - Assert.Equal(0, HelperMarshal._actionResultValue); - } - - [Fact] - public static void InvokeDelegateMethod() - { - HelperMarshal._delMethodResultValue = string.Empty; - Utils.InvokeJS(@" - var del = App.call_test_method (""CreateDelegateMethod"", [ ]); - del(""Hic sunt dracones""); - "); - - Assert.Equal("Hic sunt dracones", HelperMarshal._delMethodResultValue); - } - - [Fact] - public static void InvokeDelegateMethodReturnString() - { - HelperMarshal._delMethodStringResultValue = string.Empty; - Utils.InvokeJS(@" - var del = App.call_test_method (""CreateDelegateMethodReturnString"", [ ]); - var res = del(""Hic sunt dracones""); - App.call_test_method (""SetTestString1"", [ res ]); - "); - - Assert.Equal("Received: Hic sunt dracones", HelperMarshal._delMethodStringResultValue); - } - - [Theory] - [InlineData("CreateCustomMultiCastDelegate_VoidString", "Moin")] - [InlineData("CreateMultiCastAction_VoidString", "MoinMoin")] - public static void InvokeMultiCastDelegate_VoidString(string creator, string testStr) - { - HelperMarshal._delegateCallResult = string.Empty; - Utils.InvokeJS($@" - var del = App.call_test_method (""{creator}"", [ ]); - del(""{testStr}""); - "); - Assert.Equal($" Hello, {testStr}! GoodMorning, {testStr}!", HelperMarshal._delegateCallResult); - } - - [Theory] - [InlineData("CreateDelegateFromAnonymousMethod_VoidString")] - [InlineData("CreateDelegateFromLambda_VoidString")] - [InlineData("CreateDelegateFromMethod_VoidString")] - [InlineData("CreateActionT_VoidString")] - public static void InvokeDelegate_VoidString(string creator) - { - HelperMarshal._delegateCallResult = string.Empty; - var s = Utils.InvokeJS($@" - var del = App.call_test_method (""{creator}"", [ ]); - del(""Hic sunt dracones""); - "); - - Assert.Equal("Notification received for: Hic sunt dracones", HelperMarshal._delegateCallResult); - } - - public static IEnumerable ArrayType_TestData() - { - _objectPrototype ??= new Function("return Object.prototype.toString;"); - yield return new object[] { _objectPrototype.Call(), "Uint8Array", Uint8Array.From(new byte[10]) }; - yield return new object[] { _objectPrototype.Call(), "Array", new Array(10) }; - } - - [Theory] - [MemberData(nameof(ArrayType_TestData))] - public static void InvokeFunctionAcceptingArrayTypes(Function objectPrototype, string creator, JSObject arrayType) - { - HelperMarshal._funcActionBufferObjectResultValue = arrayType; - Assert.Equal(10, HelperMarshal._funcActionBufferObjectResultValue.GetObjectProperty("length")); - Assert.Equal($"[object {creator}]", objectPrototype.Call(HelperMarshal._funcActionBufferObjectResultValue)); - - Utils.InvokeJS($@" - var buffer = new {creator}(50); - var del = App.call_test_method (""CreateFunctionAccepting{creator}"", [ ]); - var setAction = del(buffer); - setAction(buffer); - "); - - Assert.Equal(50, HelperMarshal._funcActionBufferObjectResultValue.GetObjectProperty("length")); - Assert.Equal(HelperMarshal._funcActionBufferObjectResultValue.GetObjectProperty("length"), HelperMarshal._funcActionBufferResultLengthValue); - Assert.Equal($"[object {creator}]", objectPrototype.Call(HelperMarshal._funcActionBufferObjectResultValue)); - } - - [Fact] - public static void DispatchToDelegate() - { - var factory = new Function(@"return { - callback: null, - eventFactory:function(data){ - return { - data:data - }; - }, - fireEvent: function (evt) { - this.callback(evt); - } - };"); - var dispatcher = (JSObject)factory.Call(); - var temp = new bool[2]; - Action cb = (JSObject envt) => - { - var data = (int)envt.GetObjectProperty("data"); - temp[data] = true; - }; - dispatcher.SetObjectProperty("callback", cb); - var evnt0 = dispatcher.Invoke("eventFactory", 0); - var evnt1 = dispatcher.Invoke("eventFactory", 1); - dispatcher.Invoke("fireEvent", evnt0); - dispatcher.Invoke("fireEvent", evnt0); - dispatcher.Invoke("fireEvent", evnt1); - Assert.True(temp[0]); - Assert.True(temp[1]); - } - - [Fact] - public static void EventsAreNotCollected() - { - const int attempts = 100; // we fire 100 events in a loop, to try that it's GC same - var factory = new Function(@"return { - callback: null, - eventFactory:function(data){ - return { - data:data - }; - }, - fireEvent: function (evt) { - this.callback(evt); - } - };"); - var dispatcher = (JSObject)factory.Call(); - var temp = new bool[attempts]; - Action cb = (JSObject envt) => - { - ObjectDisposedException.ThrowIf(envt.IsDisposed, envt); - envt.AssertInFlight(0); - var data = (int)envt.GetObjectProperty("data"); - temp[data] = true; - }; - dispatcher.SetObjectProperty("callback", cb); - - var evnt = dispatcher.Invoke("eventFactory", 0); - for (int i = 0; i < attempts; i++) - { - var evnti = dispatcher.Invoke("eventFactory", i); - dispatcher.Invoke("fireEvent", evnti); - dispatcher.Invoke("fireEvent", evnt); - Utils.InvokeJS("if (globalThis.gc) globalThis.gc();");// needs v8 flag --expose-gc - } - } - - - [Fact] - public static void NullDelegate() - { - var factory = new Function("delegate", "callback", @" - callback(delegate); - "); - - Delegate check = null; - Action callback = (Delegate data) => - { - check = data; - }; - factory.Call(null, null, callback); - Assert.Null(check); - } - - [Fact] - public static async Task ResolveStringPromise() - { - var factory = new Function(@" - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve('foo'); - }, 10); - });"); - - var promise = (Task)factory.Call(); - var value = await promise; - - Assert.Equal("foo", (string)value); - - } - - [Fact] - public static async Task ResolveJSObjectPromise() - { - for (int i = 0; i < 10; i++) - { - var factory = new Function(@" - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve({foo:'bar'}); - }, 10); - });"); - - var promise = (Task)factory.Call(); - var value = (JSObject)await promise; - - Assert.Equal("bar", value.GetObjectProperty("foo")); - Utils.InvokeJS("if (globalThis.gc) globalThis.gc();");// needs v8 flag --expose-gc - } - } - - [Fact] - public static async Task RejectPromise() - { - var factory = new Function(@" - return new Promise((resolve, reject) => { - setTimeout(() => { - reject('fail'); - }, 10); - });"); - - var promise = (Task)factory.Call(); - - var ex = await Assert.ThrowsAsync(async () => await promise); - Assert.Equal("fail", ex.Message); - } - - [Fact] - public static async Task RejectPromiseError() - { - var factory = new Function(@" - return new Promise((resolve, reject) => { - setTimeout(() => { - reject(new Error('fail')); - }, 10); - });"); - - var promise = (Task)factory.Call(); - - var ex = await Assert.ThrowsAsync(async () => await promise); - Assert.Equal("Error: fail", ex.Message); - } - - - [ActiveIssue("https://github.com/dotnet/runtime/issues/56963")] - [Fact] - public static void RoundtripPromise() - { - var factory = new Function(@" - var dummy=new Promise((resolve, reject) => {}); - return { - dummy:dummy, - check:(promise)=>{ - return promise===dummy ? 1:0; - } - }"); - - var obj = (JSObject)factory.Call(); - var dummy = obj.GetObjectProperty("dummy"); - Assert.IsType>(dummy); - var check = obj.Invoke("check", dummy); - Assert.Equal(1, check); - } - - - [Fact] - public static async Task ResolveTask() - { - var tcs = new TaskCompletionSource(); - var factory = new Function("task", "callback", @" - return task.then((data)=>{ - callback(data); - }) - "); - - int check = 0; - Action callback = (int data) => - { - check = data; - }; - Task task = tcs.Task; - // we are testing that Task is marshaled as thenable - var promise = (Task)factory.Call(null, task, callback); - tcs.SetResult(1); - // the result value is not applied until we await the promise - Assert.Equal(0, check); - await promise; - // but it's set after we do - Assert.Equal(1, check); - } - - [Fact] - public static async Task RejectTask() - { - var tcs = new TaskCompletionSource(); - var factory = new Function("task", "callback", @" - return task.catch((reason)=>{ - callback(reason); - }) - "); - - string check = null; - Action callback = (string data) => - { - check = data; - }; - var promise = (Task)factory.Call(null, tcs.Task, callback); - Assert.Null(check); - tcs.SetException(new Exception("test")); - Assert.Null(check); - await promise; - Assert.Contains("System.Exception: test", check); - } - - [Fact] - public static void NullTask() - { - var tcs = new TaskCompletionSource(); - var factory = new Function("task", "callback", @" - callback(task); - "); - - Task check = Task.FromResult(1); - Action callback = (Task data) => - { - check = data; - }; - factory.Call(null, null, callback); - Assert.Null(check); - } - - [ActiveIssue("https://github.com/dotnet/runtime/issues/56963")] - [Fact] - public static void RoundtripTask() - { - var tcs = new TaskCompletionSource(); - var factory = new Function("dummy", @" - return { - dummy:dummy, - }"); - - var obj = (JSObject)factory.Call(null, tcs.Task); - var dummy = obj.GetObjectProperty("dummy"); - Assert.IsType>(dummy); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs deleted file mode 100644 index f37408f32f10d7..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs +++ /dev/null @@ -1,463 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Threading.Tasks; - -namespace System.Runtime.InteropServices.JavaScript.Tests -{ - public static class HelperMarshal - { - internal const string INTEROP_CLASS = "[System.Runtime.InteropServices.JavaScript.Legacy.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:"; - internal static int _i32Value; - private static void InvokeI32(int a, int b) - { - _i32Value = a + b; - } - - internal static float _f32Value; - private static void InvokeFloat(float f) - { - _f32Value = f; - } - - internal static double _f64Value; - private static void InvokeDouble(double d) - { - _f64Value = d; - } - - internal static long _i64Value; - private static void InvokeLong(long l) - { - _i64Value = l; - } - - internal static byte[] _byteBuffer; - private static void MarshalArrayBuffer(ArrayBuffer buffer) - { - using (var bytes = new Uint8Array(buffer)) - _byteBuffer = bytes.ToArray(); - } - - private static void MarshalByteBuffer(Uint8Array buffer) - { - _byteBuffer = buffer.ToArray(); - } - - internal static string _stringResource; - private static void InvokeString(string s) - { - _stringResource = s; - } - - internal static string _stringResource2; - private static void InvokeString2(string s) - { - _stringResource2 = s; - } - - private static string StoreArgumentAndReturnLiteral(string s) - { - _stringResource = $"s: {s} length: {s?.Length}"; - return "1"; - } - - private static string StoreAndReturnNew(string s) - { - var sb = new System.Text.StringBuilder(); - sb.Append("Got:"); - sb.Append(' '); - sb.Append(s); - _stringResource = sb.ToString(); - return _stringResource; - } - - internal static string _marshaledString; - private static string InvokeMarshalString() - { - _marshaledString = "Hic Sunt Dracones"; - return _marshaledString; - } - - internal static object _object1; - private static object InvokeObj1(object obj) - { - _object1 = obj; - return obj; - } - - internal static object _object2; - private static object InvokeObj2(object obj) - { - _object2 = obj; - return obj; - } - - internal static object _marshaledObject; - private static object InvokeMarshalObj() - { - _marshaledObject = new object(); - return _marshaledObject; - } - - private static object InvokeReturnMarshalObj() - { - return _marshaledObject; - } - - private static int InvokeReturnInt() - { - return 42; - } - - private static long InvokeReturnLong() - { - return 42L; - } - - private static double InvokeReturnDouble() - { - return double.Pi; - } - - internal static int _valOne, _valTwo; - private static void ManipulateObject(JSObject obj) - { - _valOne = (int)obj.Invoke("inc"); - _valTwo = (int)obj.Invoke("add", 20); - } - - internal static object[] _jsObjects; - private static void MinipulateObjTypes(JSObject obj) - { - _jsObjects = new object[4]; - _jsObjects[0] = obj.Invoke("return_int"); - _jsObjects[1] = obj.Invoke("return_double"); - _jsObjects[2] = obj.Invoke("return_string"); - _jsObjects[3] = obj.Invoke("return_bool"); - } - - internal static int _jsAddFunctionResult; - private static void UseFunction(JSObject obj) - { - _jsAddFunctionResult = (int)obj.Invoke("call", null, 10, 20); - } - - internal static int _jsAddAsFunctionResult; - private static void UseAsFunction(Function func) - { - _jsAddAsFunctionResult = (int)func.Call(null, 20, 30); - } - - internal static int _intValue; - private static void InvokeInt(int value) - { - _intValue = value; - } - - internal static IntPtr _intPtrValue; - private static void InvokeIntPtr(IntPtr i) - { - _intPtrValue = i; - } - - internal static IntPtr _marshaledIntPtrValue; - private static IntPtr InvokeMarshalIntPtr() - { - _marshaledIntPtrValue = (IntPtr)42; - return _marshaledIntPtrValue; - } - - internal static object[] _jsProperties; - private static void RetrieveObjectProperties(JSObject obj) - { - _jsProperties = new object[4]; - _jsProperties[0] = obj.GetObjectProperty("myInt"); - _jsProperties[1] = obj.GetObjectProperty("myDouble"); - _jsProperties[2] = obj.GetObjectProperty("myString"); - _jsProperties[3] = obj.GetObjectProperty("myBoolean"); - } - - private static void PopulateObjectProperties(JSObject obj, bool createIfNotExist) - { - _jsProperties = new object[4]; - obj.SetObjectProperty("myInt", 100, createIfNotExist); - obj.SetObjectProperty("myDouble", 4.5, createIfNotExist); - obj.SetObjectProperty("myString", "qwerty", createIfNotExist); - obj.SetObjectProperty("myBoolean", true, createIfNotExist); - } - - private static void SetTypedArrayByte(JSObject obj) - { - var dragons = "hic sunt dracones"; - byte[] buffer = System.Text.Encoding.ASCII.GetBytes(dragons); - obj.SetObjectProperty("dracones", Uint8Array.From(buffer)); - } - - internal static byte[] _taByte; - private static void GetTypedArrayByte(JSObject obj) - { - _taByte = ((Uint8Array)obj.GetObjectProperty("dracones")).ToArray(); - } - - private static Function _sumFunction; - private static void CreateFunctionSum() - { - _sumFunction = new Function("a", "b", "return a + b"); - } - - internal static int _sumValue = 0; - private static void CallFunctionSum() - { - if (_sumFunction == null) - throw new Exception("_sumFunction is null"); - _sumValue = (int)_sumFunction.Call(null, 3, 5); - } - - private static Function _mathMinFunction; - private static void CreateFunctionApply() - { - var math = (JSObject)Runtime.GetGlobalObject("Math"); - if (math == null) - throw new Exception("Runtime.GetGlobalObject(Math) returned null"); - _mathMinFunction = (Function)math.GetObjectProperty("min"); - - } - - internal static int _minValue = 0; - private static void CallFunctionApply() - { - if (_mathMinFunction == null) - throw new Exception("_mathMinFunction is null"); - _minValue = (int)_mathMinFunction.Apply(null, new object[] { 5, 6, 2, 3, 7 }); - } - - internal static Uri _blobURL; - public static void SetBlobUrl(string blobUrl) - { - _blobURL = new Uri(blobUrl); - } - - internal static Uri _blobURI; - public static void SetBlobAsUri(Uri blobUri) - { - _blobURI = blobUri; - } - - internal static uint _uintValue; - private static void InvokeUInt(uint value) - { - _uintValue = value; - } - - internal static TestEnum _enumValue; - private static void SetEnumValue(TestEnum value) - { - _enumValue = value; - } - private static TestEnum GetEnumValue() - { - return _enumValue; - } - - private static UInt64 GetUInt64() - { - return UInt64.MaxValue; - } - - internal static int _functionResultValue; - private static Func CreateFunctionDelegate() - { - return (a, b) => - { - _functionResultValue = a + b; - return _functionResultValue; - }; - } - - internal static int _functionActionResultValue; - internal static int _functionActionResultValueOfAction; - private static Func> CreateFunctionDelegateWithAction() - { - return (a, b) => - { - _functionActionResultValue = a + b; - return (i1, i2) => - { - _functionActionResultValueOfAction = i1 + i2; - }; - }; - } - - internal static int _actionResultValue; - private static Action CreateActionDelegate() - { - return (a1, a2) => - { - _actionResultValue = a1 + a2; - }; - } - - private static bool AreEqual(int a, int b) - { - return a == b; - } - - private static string TestString1(string a) - { - return "Received: " + a; - } - - private static void SetTestString1(string a) - { - _delMethodStringResultValue = a; - } - - // Create a method for a delegate. - public static void DelegateMethod(string message) - { - _delMethodResultValue = message; - } - - delegate void Del(string message); - internal static string _delMethodResultValue; - private static Del CreateDelegateMethod() - { - // Instantiate the delegate. - Del handler = DelegateMethod; - return handler; - } - - delegate string Del2(string message); - internal static string _delMethodStringResultValue; - private static Del2 CreateDelegateMethodReturnString() - { - // Instantiate the delegate. - Del2 handler = TestString1; - return handler; - } - - internal static string _delegateCallResult; - private static Del CreateDelegateFromAnonymousMethod_VoidString() - { - // Instantiate the delegate. - Del handler = delegate(string name) { _delegateCallResult = $"Notification received for: {name}"; }; - return handler; - } - - private static Del CreateDelegateFromLambda_VoidString() - { - // Instantiate the delegate. - Del handler = (string name) => { _delegateCallResult = $"Notification received for: {name}"; }; - return handler; - } - - public static void DelegateMethod_VoidString(string name) => _delegateCallResult = $"Notification received for: {name}"; - - private static Del CreateDelegateFromMethod_VoidString() - { - // Instantiate the delegate. - Del handler = DelegateMethod_VoidString; - return handler; - } - - private static Action CreateActionT_VoidString() - => (string name) => _delegateCallResult = $"Notification received for: {name}"; - - static void Hello(string s) - { - _delegateCallResult += $" Hello, {s}!"; - } - - static void GoodMorning(string s) - { - _delegateCallResult += $" GoodMorning, {s}!"; - } - - delegate void CustomDelStr(string s); - private static CustomDelStr CreateCustomMultiCastDelegate_VoidString() - { - CustomDelStr hiDel, mornDel, multiDel; - hiDel = Hello; - mornDel = GoodMorning; - multiDel = hiDel + mornDel; - - return multiDel; - } - - private static Action CreateMultiCastAction_VoidString() - { - Action hiDel, mornDel, multiDel; - hiDel = Hello; - mornDel = GoodMorning; - multiDel = hiDel + mornDel; - - return multiDel; - } - - internal static JSObject _funcActionBufferObjectResultValue; - internal static int _funcActionBufferResultLengthValue; - - private static Func> CreateFunctionAcceptingUint8Array() - { - return (buffer) => - { - _funcActionBufferObjectResultValue = buffer; - return (i1) => - { - _funcActionBufferResultLengthValue = i1.Length; - }; - }; - } - private static Func> CreateFunctionAcceptingArray() - { - return (buffer) => - { - _funcActionBufferObjectResultValue = buffer; - return (i1) => - { - _funcActionBufferResultLengthValue = i1.Length; - }; - }; - } - - public static Task SynchronousTask() - { - return Task.CompletedTask; - } - - public static async Task AsynchronousTask() - { - await Task.Yield(); - } - - public static Task SynchronousTaskInt(int i) - { - return Task.FromResult(i); - } - - public static async Task AsynchronousTaskInt(int i) - { - await Task.Yield(); - return i; - } - - public static Task FailedSynchronousTask() - { - return Task.FromException(new Exception()); - } - - public static async Task FailedAsynchronousTask() - { - await Task.Yield(); - throw new Exception(); - } - } - - public enum TestEnum : uint { - FirstValue = 1, - Zero = 0, - Five = 5, - BigValue = 0xFFFFFFFEu - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs deleted file mode 100644 index c78c422cc1b6df..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs +++ /dev/null @@ -1,215 +0,0 @@ -// 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.Linq; -using System.Threading.Tasks; -using Xunit; - -namespace System.Runtime.InteropServices.JavaScript.Tests -{ - public static class JavaScriptTests - { - [Fact] - public static void CoreTypes() - { - var arr1 = new Uint8Array(50); - Assert.Equal(50, arr1.Length); - } - - [Fact] - public static void FunctionSum() - { - // The Difference Between call() and apply() - // The difference is: - // The call() method takes arguments separately. - // The apply() method takes arguments as an array. - var sum = new Function("a", "b", "return a + b"); - Assert.Equal(8, (int)sum.Call(null, 3, 5)); - - Assert.Equal(13, (int)sum.Apply(null, new object[] { 6, 7 })); - } - - [Fact] - public static void FunctionMath() - { - JSObject math = (JSObject)Runtime.GetGlobalObject("Math"); - Assert.True(math != null, "math != null"); - - Function mathMax = (Function)math.GetObjectProperty("max"); - Assert.True(mathMax != null, "math.max != null"); - - var maxValue = (int)mathMax.Apply(null, new object[] { 5, 6, 2, 3, 7 }); - Assert.Equal(7, maxValue); - - maxValue = (int)mathMax.Call(null, 5, 6, 2, 3, 7); - Assert.Equal(7, maxValue); - - Function mathMin = (Function)((JSObject)Runtime.GetGlobalObject("Math")).GetObjectProperty("min"); - Assert.True(mathMin != null, "math.min != null"); - - var minValue = (int)mathMin.Apply(null, new object[] { 5, 6, 2, 3, 7 }); - Assert.Equal(2, minValue); - - minValue = (int)mathMin.Call(null, 5, 6, 2, 3, 7); - Assert.Equal(2, minValue); - } - - [Fact] - [OuterLoop("slow")] - public static async Task BagIterator() - { - await Task.Delay(1); - - var bagFn = new Function(@" - var same = { - x:1 - }; - return Object.entries({ - a:1, - b:'two', - c:{fold:{}}, - d:same, - e:same, - f:same - }); - "); - - for (int attempt = 0; attempt < 100_000; attempt++) - { - try - { - using var bag = (JSObject)bagFn.Call(null); - using var entriesIterator = (JSObject)bag.Invoke("entries"); - - var cnt = entriesIterator.ToEnumerable().Count(); - Assert.Equal(6, cnt); - - // fill GC helps to repro - var x = new byte[100 + attempt / 100]; - if (attempt % 1000 == 0) - { - Utils.InvokeJS("if (globalThis.gc) globalThis.gc();");// needs v8 flag --expose-gc - GC.Collect(); - } - } - catch (Exception ex) - { - throw new Exception(ex.Message + " At attempt=" + attempt, ex); - } - } - } - - [Fact] - public static async Task Iterator() - { - await Task.Delay(1); - - var makeRangeIterator = new Function("start", "end", "step", @" - let nextIndex = start; - let iterationCount = 0; - - const rangeIterator = { - next: function() { - let result; - if (nextIndex < end) { - result = { value: {}, done: false } - nextIndex += step; - iterationCount++; - return result; - } - return { value: {}, done: true } - } - }; - return rangeIterator; - "); - - const int count = 500; - for (int attempt = 0; attempt < 100; attempt++) - { - int index = 0; - try - { - var entriesIterator = (JSObject)makeRangeIterator.Call(null, 0, count, 1); - Assert.NotNull(entriesIterator); - using (entriesIterator) - { - var enumerable = entriesIterator.ToEnumerable(); - var enumerator = enumerable.GetEnumerator(); - Assert.NotNull(enumerator); - - using (enumerator) - { - while (enumerator.MoveNext()) - { - Assert.NotNull(enumerator.Current); - index++; - } - } - } - Assert.Equal(count, index); - } - catch (Exception ex) - { - throw new Exception($"At attempt={attempt}, index={index}: {ex.Message}", ex); - } - await Task.Yield(); - } - } - - public static IEnumerable ToEnumerable(this JSObject iterrator) - { - JSObject nextResult = null; - try - { - nextResult = (JSObject)iterrator.Invoke("next"); - var done = (bool)nextResult.GetObjectProperty("done"); - while (!done) - { - object value = nextResult.GetObjectProperty("value"); - nextResult.Dispose(); - yield return value; - nextResult = (JSObject)iterrator.Invoke("next"); - done = (bool)nextResult.GetObjectProperty("done"); - } - } - finally - { - nextResult?.Dispose(); - } - } - - [Fact] - public static void RoundtripCSDate() - { - var factory = new Function("dummy", @" - return { - dummy:dummy, - }"); - var date = new DateTime(2021, 01, 01, 12, 34, 45); - - var obj = (JSObject)factory.Call(null, date); - var dummy = (DateTime)obj.GetObjectProperty("dummy"); - Assert.Equal(date, dummy); - } - - [Fact] - public static void RoundtripJSDate() - { - var factory = new Function(@" - var dummy = new Date(2021, 00, 01, 12, 34, 45, 567); - return { - dummy:dummy, - check:(value) => { - return value.valueOf()==dummy.valueOf() ? 1 : 0; - }, - }"); - var obj = (JSObject)factory.Call(); - - var date = (DateTime)obj.GetObjectProperty("dummy"); - var check = (int)obj.Invoke("check", date); - Assert.Equal(1, check); - } - - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs deleted file mode 100644 index 3d27ba7948c248..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs +++ /dev/null @@ -1,791 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Threading.Tasks; -using Xunit; - -namespace System.Runtime.InteropServices.JavaScript.Tests -{ - public static class MarshalTests - { - [Fact] - public static void MarshalPrimitivesToCS() - { - HelperMarshal._i32Value = 0; - Utils.InvokeJS("App.call_test_method (\"InvokeI32\", [10, 20])"); - Assert.Equal(30, HelperMarshal._i32Value); - - HelperMarshal._f32Value = 0; - Utils.InvokeJS("App.call_test_method (\"InvokeFloat\", [1.5])"); - Assert.Equal(1.5f, HelperMarshal._f32Value); - - HelperMarshal._f64Value = 0; - Utils.InvokeJS("App.call_test_method (\"InvokeDouble\", [4.5])"); - Assert.Equal(4.5, HelperMarshal._f64Value); - - HelperMarshal._i64Value = 0; - Utils.InvokeJS("App.call_test_method (\"InvokeLong\", [99])"); - Assert.Equal(99, HelperMarshal._i64Value); - } - - [Fact] - public static void MarshalArrayBuffer() - { - Utils.InvokeJS(@" - var buffer = new ArrayBuffer(16); - App.call_test_method (""MarshalArrayBuffer"", [ buffer ]); - "); - Assert.Equal(16, HelperMarshal._byteBuffer.Length); - } - - [Fact] - public static void MarshalStringToCS() - { - HelperMarshal._stringResource = null; - Utils.InvokeJS("App.call_test_method(\"InvokeString\", [\"hello\"])"); - Assert.Equal("hello", HelperMarshal._stringResource); - } - - [Fact] - public static void MarshalUnicodeStringToCS() - { - HelperMarshal._stringResource = null; - Utils.InvokeJS("App.call_test_method(\"StoreAndReturnNew\", [' '+\"\u0050\u0159\u00ed\u006c\u0069\u0161\u0020\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd\u0020\u006b\u016f\u0148\u202f\u00fa\u0070\u011b\u006c\u0020\u010f\u00e1\u0062\u0065\u006c\u0073\u006b\u00e9\u0020\u00f3\u0064\u0079\"])"); - Assert.Equal("Got: \u0050\u0159\u00ed\u006c\u0069\u0161\u0020\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd\u0020\u006b\u016f\u0148\u202f\u00fa\u0070\u011b\u006c\u0020\u010f\u00e1\u0062\u0065\u006c\u0073\u006b\u00e9\u0020\u00f3\u0064\u0079", HelperMarshal._stringResource); - - HelperMarshal._stringResource = null; - Utils.InvokeJS("App.call_test_method(\"StoreAndReturnNew\", [' '+\"\uFEFF\u0000\uFFFE\"])"); - Assert.Equal("Got: \uFEFF\0\uFFFE", HelperMarshal._stringResource); - - HelperMarshal._stringResource = null; - Utils.InvokeJS("App.call_test_method(\"StoreAndReturnNew\", [' '+\"\u02F3o\u0302\u0303\u0308\u0930\u0903\u0951\"])"); - Assert.Equal("Got: \u02F3o\u0302\u0303\u0308\u0930\u0903\u0951", HelperMarshal._stringResource); - } - - [Fact] - public static void MarshalNullStringToCS() - { - HelperMarshal._stringResource = null; - Utils.InvokeJS("App.call_test_method(\"InvokeString\", [ null ])"); - Assert.Null(HelperMarshal._stringResource); - } - - [Fact] - public static void MarshalStringToJS() - { - HelperMarshal._marshaledString = HelperMarshal._stringResource = null; - Utils.InvokeJS(@" - var str = App.call_test_method (""InvokeMarshalString""); - App.call_test_method (""InvokeString"", [ str ]); - "); - Assert.NotNull(HelperMarshal._marshaledString); - Assert.Equal(HelperMarshal._marshaledString, HelperMarshal._stringResource); - } - - [Fact] - public static void JSObjectKeepIdentityAcrossCalls() - { - HelperMarshal._object1 = HelperMarshal._object2 = null; - Utils.InvokeJS(@" - var obj = { foo: 10 }; - var res = App.call_test_method (""InvokeObj1"", [ obj ]); - App.call_test_method (""InvokeObj2"", [ res ]); - "); - - Assert.NotNull(HelperMarshal._object1); - Assert.Same(HelperMarshal._object1, HelperMarshal._object2); - } - - [Fact] - public static void CSObjectKeepIdentityAcrossCalls() - { - HelperMarshal._marshaledObject = HelperMarshal._object1 = HelperMarshal._object2 = null; - Utils.InvokeJS(@" - var obj = App.call_test_method (""InvokeMarshalObj""); - var res = App.call_test_method (""InvokeObj1"", [ obj ]); - App.call_test_method (""InvokeObj2"", [ res ]); - "); - - Assert.NotNull(HelperMarshal._object1); - Assert.Same(HelperMarshal._marshaledObject, HelperMarshal._object1); - Assert.Same(HelperMarshal._object1, HelperMarshal._object2); - } - - [Theory] - [InlineData(byte.MinValue)] - [InlineData(byte.MaxValue)] - [InlineData(SByte.MinValue)] - [InlineData(SByte.MaxValue)] - [InlineData(uint.MaxValue)] - [InlineData(uint.MinValue)] - [InlineData(int.MaxValue)] - [InlineData(int.MinValue)] - [InlineData(double.MaxValue)] - [InlineData(double.MinValue)] - public static void InvokeUnboxNumberString(object o) - { - HelperMarshal._marshaledObject = o; - HelperMarshal._object1 = HelperMarshal._object2 = null; - var value = Utils.InvokeJS(@" - var obj = App.call_test_method (""InvokeReturnMarshalObj""); - var res = App.call_test_method (""InvokeObj1"", [ obj.toString() ]); - "); - - Assert.Equal(o.ToString().ToLower(), HelperMarshal._object1); - } - - [Theory] - [InlineData(byte.MinValue, 0)] - [InlineData(byte.MaxValue, 255)] - [InlineData(SByte.MinValue, -128)] - [InlineData(SByte.MaxValue, 127)] - [InlineData(uint.MaxValue)] - [InlineData(uint.MinValue, 0)] - [InlineData(int.MaxValue)] - [InlineData(int.MinValue)] - [InlineData(double.MaxValue)] - [InlineData(double.MinValue)] - public static void InvokeUnboxNumber(object o, object expected = null) - { - HelperMarshal._marshaledObject = o; - HelperMarshal._object1 = HelperMarshal._object2 = null; - Utils.InvokeJS(@" - var obj = App.call_test_method (""InvokeReturnMarshalObj""); - var res = App.call_test_method (""InvokeObj1"", [ obj ]); - "); - - Assert.Equal(expected ?? o, HelperMarshal._object1); - } - - [Fact] - public static void InvokeUnboxInt() - { - Utils.InvokeJS(@" - var obj = App.call_test_method (""InvokeReturnInt""); - var res = App.call_test_method (""InvokeObj1"", [ obj ]); - "); - - Assert.Equal(42, HelperMarshal._object1); - } - - [Fact] - public static void InvokeUnboxDouble() - { - Utils.InvokeJS(@" - var obj = App.call_test_method (""InvokeReturnDouble""); - var res = App.call_test_method (""InvokeObj1"", [ obj ]); - "); - - Assert.Equal(double.Pi, HelperMarshal._object1); - } - - [Fact] - public static void InvokeUnboxLongFail() - { - var ex = Assert.Throws(() => Utils.InvokeJS(@" - console.log(""the exception in InvokeReturnLong after this is intentional""); - App.call_test_method (""InvokeReturnLong""); - ")); - Assert.Contains("int64 not available", ex.Message); - } - - [Theory] - [InlineData(byte.MinValue, 0)] - [InlineData(byte.MaxValue, 255)] - [InlineData(SByte.MinValue, -128)] - [InlineData(SByte.MaxValue, 127)] - [InlineData(uint.MaxValue)] - [InlineData(uint.MinValue, 0)] - [InlineData(int.MaxValue)] - [InlineData(int.MinValue)] - [InlineData(double.MaxValue)] - [InlineData(double.MinValue)] - public static void InvokeUnboxStringNumber(object o, object expected = null) - { - HelperMarshal._marshaledObject = HelperMarshal._object1 = HelperMarshal._object2 = null; - Utils.InvokeJS(String.Format(@" - var res = App.call_test_method (""InvokeObj1"", [ {0} ]); - ", o)); - - Assert.Equal(expected ?? o, HelperMarshal._object1); - } - - [Fact] - public static void JSInvokeInt() - { - Utils.InvokeJS(@" - var obj = { - foo: 10, - inc: function() { - var c = this.foo; - ++this.foo; - return c; - }, - add: function(val){ - return this.foo + val; - } - }; - App.call_test_method (""ManipulateObject"", [ obj ]); - "); - Assert.Equal(10, HelperMarshal._valOne); - Assert.Equal(31, HelperMarshal._valTwo); - } - - [Fact] - public static void JSInvokeTypes() - { - Utils.InvokeJS(@" - var obj = { - return_int: function() { return 100; }, - return_double: function() { return 4.5; }, - return_string: function() { return 'Hic Sunt Dracones'; }, - return_bool: function() { return true; }, - }; - App.call_test_method (""MinipulateObjTypes"", [ obj ]); - "); - - Assert.Equal(100, HelperMarshal._jsObjects[0]); - Assert.Equal(4.5, HelperMarshal._jsObjects[1]); - Assert.Equal("Hic Sunt Dracones", HelperMarshal._jsObjects[2]); - Assert.NotEqual("HIC SVNT LEONES", HelperMarshal._jsObjects[2]); - Assert.Equal(true, HelperMarshal._jsObjects[3]); - } - - [Fact] - public static void JSObjectApply() - { - Utils.InvokeJS(@" - var do_add = function(a, b) { return a + b }; - App.call_test_method (""UseFunction"", [ do_add ]); - "); - Assert.Equal(30, HelperMarshal._jsAddFunctionResult); - } - - [Fact] - public static void JSObjectAsFunction() - { - Utils.InvokeJS(@" - var do_add = function(a, b) { return a + b }; - App.call_test_method (""UseAsFunction"", [ do_add ]); - "); - Assert.Equal(50, HelperMarshal._jsAddAsFunctionResult); - } - - [Fact] - public static void BindStaticMethod() - { - HelperMarshal._intValue = 0; - Utils.InvokeJS(@$" - var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - invoke_int (200); - "); - - Assert.Equal(200, HelperMarshal._intValue); - } - - [Fact] - public static void BindIntPtrStaticMethod() - { - HelperMarshal._intPtrValue = IntPtr.Zero; - Utils.InvokeJS(@$" - var invoke_int_ptr = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeIntPtr""); - invoke_int_ptr (42); - "); - Assert.Equal(42, (int)HelperMarshal._intPtrValue); - } - - [Fact] - public static void MarshalIntPtrToJS() - { - HelperMarshal._marshaledIntPtrValue = IntPtr.Zero; - Utils.InvokeJS(@$" - var invokeMarshalIntPtr = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeMarshalIntPtr""); - var r = invokeMarshalIntPtr (); - - if (r != 42) throw `Invalid int_ptr value`; - "); - Assert.Equal(42, (int)HelperMarshal._marshaledIntPtrValue); - } - - [Fact] - public static void ResolveMethod() - { - HelperMarshal._intValue = 0; - Utils.InvokeJS(@$" - var invoke_int = INTERNAL.mono_method_resolve (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - App.call_test_method (""InvokeInt"", [ invoke_int ]); - "); - - Assert.NotEqual(0, HelperMarshal._intValue); - } - - [Fact] - public static void GetObjectProperties() - { - Utils.InvokeJS(@" - var obj = {myInt: 100, myDouble: 4.5, myString: ""Hic Sunt Dracones"", myBoolean: true}; - App.call_test_method (""RetrieveObjectProperties"", [ obj ]); - "); - - Assert.Equal(100, HelperMarshal._jsProperties[0]); - Assert.Equal(4.5, HelperMarshal._jsProperties[1]); - Assert.Equal("Hic Sunt Dracones", HelperMarshal._jsProperties[2]); - Assert.Equal(true, HelperMarshal._jsProperties[3]); - } - - [Fact] - public static void SetObjectProperties() - { - Utils.InvokeJS(@" - var obj = {myInt: 200, myDouble: 0, myString: ""foo"", myBoolean: false}; - App.call_test_method (""PopulateObjectProperties"", [ obj, false ]); - App.call_test_method (""RetrieveObjectProperties"", [ obj ]); - "); - - Assert.Equal(100, HelperMarshal._jsProperties[0]); - Assert.Equal(4.5, HelperMarshal._jsProperties[1]); - Assert.Equal("qwerty", HelperMarshal._jsProperties[2]); - Assert.Equal(true, HelperMarshal._jsProperties[3]); - } - - [Fact] - public static void SetObjectPropertiesIfNotExistsFalse() - { - // This test will not create the properties if they do not already exist - Utils.InvokeJS(@" - var obj = {myInt: 200}; - App.call_test_method (""PopulateObjectProperties"", [ obj, false ]); - App.call_test_method (""RetrieveObjectProperties"", [ obj ]); - "); - - Assert.Equal(100, HelperMarshal._jsProperties[0]); - Assert.Null(HelperMarshal._jsProperties[1]); - Assert.Null(HelperMarshal._jsProperties[2]); - Assert.Null(HelperMarshal._jsProperties[3]); - } - - [Fact] - public static void SetObjectPropertiesIfNotExistsTrue() - { - // This test will set the value of the property if it exists and will create and - // set the value if it does not exists - Utils.InvokeJS(@" - var obj = {myInt: 200}; - App.call_test_method (""PopulateObjectProperties"", [ obj, true ]); - App.call_test_method (""RetrieveObjectProperties"", [ obj ]); - "); - - Assert.Equal(100, HelperMarshal._jsProperties[0]); - Assert.Equal(4.5, HelperMarshal._jsProperties[1]); - Assert.Equal("qwerty", HelperMarshal._jsProperties[2]); - Assert.Equal(true, HelperMarshal._jsProperties[3]); - } - - [Fact] - public static void MarshalTypedArray() - { - Utils.InvokeJS(@" - var buffer = new ArrayBuffer(16); - var uint8View = new Uint8Array(buffer); - App.call_test_method (""MarshalByteBuffer"", [ uint8View ]); - "); - - Assert.Equal(16, HelperMarshal._byteBuffer.Length); - } - - [Fact] - public static void MarshalUri() - { - HelperMarshal._blobURI = null; - Utils.InvokeJS(@" - App.call_test_method (""SetBlobAsUri"", [ ""https://dotnet.microsoft.com/en-us/"" ]); - "); - - Assert.NotNull(HelperMarshal._blobURI); - } - - private static void RunMarshalTypedArrayJS(string type) - { - Utils.InvokeJS(@" - var obj = { }; - App.call_test_method (""SetTypedArray" + type + @""", [ obj ]); - App.call_test_method (""GetTypedArray" + type + @""", [ obj ]); - "); - } - - [Fact] - public static void MarshalTypedArrayByte() - { - RunMarshalTypedArrayJS("Byte"); - Assert.Equal(17, HelperMarshal._taByte.Length); - Assert.Equal(104, HelperMarshal._taByte[0]); - Assert.Equal(115, HelperMarshal._taByte[HelperMarshal._taByte.Length - 1]); - Assert.Equal("hic sunt dracones", System.Text.Encoding.Default.GetString(HelperMarshal._taByte)); - } - - [Fact] - public static void TestFunctionSum() - { - HelperMarshal._sumValue = 0; - Utils.InvokeJS(@" - App.call_test_method (""CreateFunctionSum"", []); - App.call_test_method (""CallFunctionSum"", []); - "); - Assert.Equal(8, HelperMarshal._sumValue); - } - - [Fact] - public static void TestFunctionApply() - { - HelperMarshal._minValue = 0; - Utils.InvokeJS(@" - App.call_test_method (""CreateFunctionApply"", []); - App.call_test_method (""CallFunctionApply"", []); - "); - Assert.Equal(2, HelperMarshal._minValue); - } - - [Fact] - public static void BoundStaticMethodMissingArgs() - { - HelperMarshal._intValue = 1; - var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - invoke_int (); - ")); - Assert.Contains("Value is not an integer: undefined (undefined)", ex.Message); - Assert.Equal(1, HelperMarshal._intValue); - } - - [Fact] - public static void BoundStaticMethodExtraArgs() - { - HelperMarshal._intValue = 0; - Utils.InvokeJS(@$" - var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - invoke_int (200, 400); - "); - Assert.Equal(200, HelperMarshal._intValue); - } - - [Fact] - public static void RangeCheckInt() - { - HelperMarshal._intValue = 0; - // no numbers bigger than 32 bits - var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - invoke_int (Number.MAX_SAFE_INTEGER); - ")); - Assert.Contains("Overflow: value 9007199254740991 is out of -2147483648 2147483647 range", ex.Message); - Assert.Equal(0, HelperMarshal._intValue); - } - - [Fact] - public static void IntegerCheckInt() - { - HelperMarshal._intValue = 0; - // no floating point rounding - var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - invoke_int (3.14); - ")); - Assert.Contains("Value is not an integer: 3.14 (number)", ex.Message); - Assert.Equal(0, HelperMarshal._intValue); - } - - [Fact] - public static void TypeCheckInt() - { - HelperMarshal._intValue = 0; - // no string conversion - var ex = Assert.Throws(() => Utils.InvokeJS(@$" - var invoke_int = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeInt""); - invoke_int (""200""); - ")); - Assert.Contains("Value is not an integer: 200 (string)", ex.Message); - Assert.Equal(0, HelperMarshal._intValue); - } - - [Fact] - public static void PassUintArgument() - { - HelperMarshal._uintValue = 0; - Utils.InvokeJS(@$" - var invoke_uint = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); - invoke_uint (0xFFFFFFFE); - "); - - Assert.Equal(0xFFFFFFFEu, HelperMarshal._uintValue); - } - - [Fact] - public static void ReturnUintEnum() - { - HelperMarshal._uintValue = 0; - HelperMarshal._enumValue = TestEnum.BigValue; - Utils.InvokeJS(@$" - var get_value = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetEnumValue""); - var e = get_value (); - var invoke_uint = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}InvokeUInt""); - invoke_uint (e); - "); - Assert.Equal((uint)TestEnum.BigValue, HelperMarshal._uintValue); - } - - [Fact] - public static void PassUintEnumByValue() - { - HelperMarshal._enumValue = TestEnum.Zero; - Utils.InvokeJS(@$" - var set_enum = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); - set_enum (0xFFFFFFFE); - "); - Assert.Equal(TestEnum.BigValue, HelperMarshal._enumValue); - } - - [Fact] - public static void PassUintEnumByNameIsNotImplemented() - { - HelperMarshal._enumValue = TestEnum.Zero; - var exc = Assert.Throws(() => - Utils.InvokeJS(@$" - var set_enum = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}SetEnumValue"", ""j""); - set_enum (""BigValue""); - ") - ); - Assert.StartsWith("Error: Expected numeric value for enum argument, got 'BigValue'", exc.Message); - } - - [Fact] - public static void CannotUnboxUint64() - { - var exc = Assert.Throws(() => - Utils.InvokeJS(@$" - var get_u64 = BINDING.bind_static_method (""{HelperMarshal.INTEROP_CLASS}GetUInt64"", """"); - var u64 = get_u64(); - ") - ); - Assert.StartsWith("Error: int64 not available", exc.Message); - } - - [Fact] - public static void BareStringArgumentsAreNotInterned() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - Utils.InvokeJS(@" - var jsLiteral = ""hello world""; - App.call_test_method (""InvokeString"", [ jsLiteral ]); - App.call_test_method (""InvokeString2"", [ jsLiteral ]); - "); - Assert.Equal("hello world", HelperMarshal._stringResource); - Assert.Equal(HelperMarshal._stringResource, HelperMarshal._stringResource2); - Assert.False(Object.ReferenceEquals(HelperMarshal._stringResource, HelperMarshal._stringResource2)); - } - - [Fact] - public static void InternedStringSignaturesAreInternedOnJavascriptSide() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - Utils.InvokeJS(@" - var sym = ""interned string""; - App.call_test_method (""InvokeString"", [ sym ], ""S""); - App.call_test_method (""InvokeString2"", [ sym ], ""S""); - "); - Assert.Equal("interned string", HelperMarshal._stringResource); - Assert.Equal(HelperMarshal._stringResource, HelperMarshal._stringResource2); - Assert.True(Object.ReferenceEquals(HelperMarshal._stringResource, HelperMarshal._stringResource2)); - } - - [Fact] - public static void OnceAJSStringIsInternedItIsAlwaysUsedIfPossible() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - Utils.InvokeJS(@" - var sym = ""interned string 2""; - App.call_test_method (""InvokeString"", [ sym ], ""S""); - App.call_test_method (""InvokeString2"", [ sym ], ""s""); - "); - Assert.Equal("interned string 2", HelperMarshal._stringResource); - Assert.Equal(HelperMarshal._stringResource, HelperMarshal._stringResource2); - Assert.True(Object.ReferenceEquals(HelperMarshal._stringResource, HelperMarshal._stringResource2)); - } - - [Fact] - public static void ManuallyInternString() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - Utils.InvokeJS(@" - var sym = INTERNAL.stringToMonoStringIntern(""interned string 3""); - App.call_test_method (""InvokeString"", [ sym ], ""s""); - App.call_test_method (""InvokeString2"", [ sym ], ""s""); - "); - Assert.Equal("interned string 3", HelperMarshal._stringResource); - Assert.Equal(HelperMarshal._stringResource, HelperMarshal._stringResource2); - Assert.True(Object.ReferenceEquals(HelperMarshal._stringResource, HelperMarshal._stringResource2)); - } - - [Fact] - public static void LargeStringsAreNotAutomaticallyLocatedInInternTable() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - Utils.InvokeJS(@" - var s = ""long interned string""; - for (var i = 0; i < 1024; i++) - s += String(i % 10); - var sym = INTERNAL.stringToMonoStringIntern(s); - App.call_test_method (""InvokeString"", [ sym ], ""S""); - App.call_test_method (""InvokeString2"", [ sym ], ""s""); - "); - Assert.Equal(HelperMarshal._stringResource, HelperMarshal._stringResource2); - Assert.False(Object.ReferenceEquals(HelperMarshal._stringResource, HelperMarshal._stringResource2)); - } - - [Fact] - public static void CanInternVeryManyStrings() - { - HelperMarshal._stringResource = null; - Utils.InvokeJS(@" - for (var i = 0; i < 10240; i++) - INTERNAL.stringToMonoStringIntern('s' + i); - App.call_test_method (""InvokeString"", [ 's5000' ], ""S""); - "); - Assert.Equal("s5000", HelperMarshal._stringResource); - Assert.Equal(HelperMarshal._stringResource, string.IsInterned(HelperMarshal._stringResource)); - } - - [Fact] - public static void SymbolsAreMarshaledAsStrings() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - Utils.InvokeJS(@" - var jsLiteral = Symbol(""custom symbol""); - App.call_test_method (""InvokeString"", [ jsLiteral ]); - App.call_test_method (""InvokeString2"", [ jsLiteral ]); - "); - Assert.Equal("custom symbol", HelperMarshal._stringResource); - Assert.Equal(HelperMarshal._stringResource, HelperMarshal._stringResource2); - Assert.True(Object.ReferenceEquals(HelperMarshal._stringResource, HelperMarshal._stringResource2)); - } - - [Fact] - public static void InternedStringReturnValuesWork() - { - HelperMarshal._stringResource = HelperMarshal._stringResource2 = null; - var fqn = "[System.Runtime.InteropServices.JavaScript.Legacy.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:StoreArgumentAndReturnLiteral"; - Utils.InvokeJS( - $"var a = BINDING.bind_static_method('{fqn}')('test');\r\n" + - $"var b = BINDING.bind_static_method('{fqn}')(a);\r\n" + - "App.call_test_method ('InvokeString2', [ b ]);" - ); - Assert.Equal("s: 1 length: 1", HelperMarshal._stringResource); - Assert.Equal("1", HelperMarshal._stringResource2); - } - - [Fact] - public static void InvokeJSExpression() - { - var result = Utils.InvokeJS(@"1 + 2"); - Assert.Equal("3", result); - } - - [Fact] - public static void InvokeJSNullExpression() - { - var result = Utils.InvokeJS(@"null"); - Assert.Null(result); - } - - [Fact] - public static void InvokeJSUndefinedExpression() - { - var result = Utils.InvokeJS(@"undefined"); - Assert.Null(result); - } - - [Fact] - public static void InvokeJSNotInGlobalScope() - { - var result = Utils.InvokeJS(@"var test_local_variable_name = 5; globalThis.test_local_variable_name"); - Assert.Null(result); - } - - private static async Task MarshalTask(string helperMethodName, string helperMethodArgs = "", string resolvedBody = "") - { - Utils.InvokeJS( - @"globalThis.__test_promise_completed = false; " + - @"globalThis.__test_promise_resolved = false; " + - @"globalThis.__test_promise_failed = false; " + - $@"var t = App.call_test_method ('{helperMethodName}', [ {helperMethodArgs} ]); " + - "t.then(result => { globalThis.__test_promise_resolved = true; " + resolvedBody + " })" + - " .catch(e => { globalThis.__test_promise_failed = true; })" + - " .finally(result => { globalThis.__test_promise_completed = true; }); " + - "" - ); - - await Task.Delay(1); - - var completed = bool.Parse(Utils.InvokeJS(@"globalThis.__test_promise_completed")); - Assert.True(completed, "JavasScript promise did not completed."); - - var resolved = bool.Parse(Utils.InvokeJS(@"globalThis.__test_promise_resolved")); - return resolved; - } - - private static async Task MarshalTaskReturningInt(string helperMethodName) - { - HelperMarshal._intValue = 0; - - bool success = await MarshalTask(helperMethodName, "7", "App.call_test_method ('InvokeInt', [ result ], 'i');"); - - Assert.True(success, $"{helperMethodName} didn't succeeded."); - Assert.Equal(7, HelperMarshal._intValue); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/94253", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] - public static async Task MarshalSynchronousTask() - { - bool success = await MarshalTask("SynchronousTask"); - Assert.True(success, "SynchronousTask didn't succeeded."); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/94253", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] - public static async Task MarshalAsynchronousTask() - { - bool success = await MarshalTask("AsynchronousTask"); - Assert.True(success, "AsynchronousTask didn't succeeded."); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/94253", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] - public static Task MarshalSynchronousTaskInt() - { - return MarshalTaskReturningInt("SynchronousTaskInt"); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/94253", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] - public static Task MarshalAsynchronousTaskInt() - { - return MarshalTaskReturningInt("AsynchronousTaskInt"); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/94253", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] - public static async Task MarshalFailedSynchronousTask() - { - bool success = await MarshalTask("FailedSynchronousTask"); - Assert.False(success, "FailedSynchronousTask didn't failed."); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/94253", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] - public static async Task MarshalFailedAsynchronousTask() - { - bool success = await MarshalTask("FailedAsynchronousTask"); - Assert.False(success, "FailedAsynchronousTask didn't failed."); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MemoryTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MemoryTests.cs deleted file mode 100644 index 70eb88b1a55c6a..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/MemoryTests.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using Xunit; - -namespace System.Runtime.InteropServices.JavaScript.Tests -{ - public class MemoryTests - { - [Theory] - [InlineData(-1L)] - [InlineData(-42L)] - [InlineData(int.MinValue)] - [InlineData(-9007199254740990L)]//MIN_SAFE_INTEGER+1 - [InlineData(-9007199254740991L)]//MIN_SAFE_INTEGER - [InlineData(1L)] - [InlineData(0L)] - [InlineData(42L)] - [InlineData(int.MaxValue)] - [InlineData(0xF_FFFF_FFFFL)] - [InlineData(9007199254740991L)]//MAX_SAFE_INTEGER - public static unsafe void Int52TestOK(long value) - { - long expected = value; - long dummy = 0xA6A6A6A6L; - long actual2 = dummy; - var bagFn = new Function("ptr", "ptr2", @" - const value=globalThis.App.runtime.getHeapI52(ptr); - globalThis.App.runtime.setHeapI52(ptr2, value); - return ''+value;"); - - uint ptr = (uint)Unsafe.AsPointer(ref expected); - uint ptr2 = (uint)Unsafe.AsPointer(ref actual2); - - object actual = (string)bagFn.Call(null, ptr, ptr2); - Assert.Equal(""+ value, actual); - Assert.Equal(value, actual2); - Assert.Equal(0xA6A6A6A6L, dummy); - } - - [Theory] - [InlineData(uint.MinValue)] - [InlineData(1UL)] - [InlineData(0UL)] - [InlineData(42UL)] - [InlineData(uint.MaxValue)] - [InlineData(0xF_FFFF_FFFFUL)] - [InlineData(9007199254740991UL)]//MAX_SAFE_INTEGER - public static unsafe void UInt52TestOK(ulong value) - { - ulong expected = value; - ulong dummy = 0xA6A6A6A6UL; - ulong actual2 = dummy; - var bagFn = new Function("ptr", "ptr2", @" - const value=globalThis.App.runtime.getHeapI52(ptr); - globalThis.App.runtime.setHeapU52(ptr2, value); - return ''+value;"); - - uint ptr = (uint)Unsafe.AsPointer(ref expected); - uint ptr2 = (uint)Unsafe.AsPointer(ref actual2); - - string actual = (string)bagFn.Call(null, ptr, ptr2); - Assert.Equal(""+value, actual); - Assert.Equal(value, actual2); - Assert.Equal(0xA6A6A6A6UL, dummy); - } - - [Fact] - public static unsafe void UInt52TestRandom() - { - for(int i = 0; i < 1000; i++) - { - var value = (ulong)Random.Shared.NextInt64(); - value&= 0x1F_FFFF_FFFF_FFFFUL;// only safe range - UInt52TestOK(value); - } - } - - [Fact] - public static unsafe void Int52TestRandom() - { - for(int i = 0; i < 1000; i++) - { - var value = Random.Shared.NextInt64(-9007199254740991L, 9007199254740991L); - Int52TestOK(value); - } - } - - [Theory] - [InlineData(double.NegativeInfinity)] - [InlineData(double.PositiveInfinity)] - [InlineData(double.MinValue)] - [InlineData(double.MaxValue)] - [InlineData(double.Pi)] - [InlineData(9007199254740993.0)]//MAX_SAFE_INTEGER +2 - public static unsafe void Int52TestRange(double value) - { - long actual = 0; - uint ptr = (uint)Unsafe.AsPointer(ref actual); - var bagFn = new Function("ptr", "value", @" - globalThis.App.runtime.setHeapI52(ptr, value);"); - var ex=Assert.Throws(() => bagFn.Call(null, ptr, value)); - Assert.Contains("Value is not a safe integer", ex.Message); - - double expectedD = value; - uint ptrD = (uint)Unsafe.AsPointer(ref expectedD); - var bagFnD = new Function("ptr", "value", @" - globalThis.App.runtime.getHeapI52(ptr);"); - var exD = Assert.Throws(() => bagFn.Call(null, ptr, value)); - Assert.Contains("Value is not a safe integer", ex.Message); - } - - [Theory] - [InlineData(-1.0)] - public static unsafe void UInt52TestRange(double value) - { - long actual = 0; - uint ptr = (uint)Unsafe.AsPointer(ref actual); - var bagFn = new Function("ptr", "value", @" - globalThis.App.runtime.setHeapU52(ptr, value);"); - var ex=Assert.Throws(() => bagFn.Call(null, ptr, value)); - Assert.Contains("Can't convert negative Number into UInt64", ex.Message); - - double expectedD = value; - uint ptrD = (uint)Unsafe.AsPointer(ref expectedD); - var bagFnD = new Function("ptr", "value", @" - globalThis.App.runtime.getHeapU52(ptr);"); - var exD = Assert.Throws(() => bagFn.Call(null, ptr, value)); - Assert.Contains("Can't convert negative Number into UInt64", ex.Message); - } - - [Fact] - public static unsafe void Int52TestNaN() - { - long actual = 0; - uint ptr = (uint)Unsafe.AsPointer(ref actual); - var bagFn = new Function("ptr", "value", @" - globalThis.App.runtime.setHeapI52(ptr, value);"); - var ex=Assert.Throws(() => bagFn.Call(null, ptr, double.NaN)); - Assert.Contains("Value is not a safe integer: NaN (number)", ex.Message); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ParallelTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ParallelTests.cs deleted file mode 100644 index c74b84b60fc092..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/ParallelTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -// 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; -using System.Collections.Generic; -using System.Runtime.InteropServices.JavaScript; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace System.Runtime.InteropServices.JavaScript.Tests -{ - public static class ParallelTests - { - // The behavior of APIs like Invoke depends on how many items they are asked to invoke - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - [InlineData(5)] - [InlineData(32)] - [InlineData(250)] - public static void ParallelInvokeActionArray(int count) - { - var actions = new List(); - int sum = 0, expected = 0; - for (int i = 0; i < count; i++) { - int j = i; - actions.Add(() => { - sum += j; - }); - expected += j; - } - - Parallel.Invoke(actions.ToArray()); - Assert.Equal(expected, sum); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(32)] - [InlineData(250)] - public static void ParallelFor(int count) - { - int sum = 0, expected = 0; - for (int i = 0; i < count; i++) - expected += i; - Parallel.For(0, count, (i) => { sum += i; }); - Assert.Equal(expected, sum); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(32)] - [InlineData(250)] - public static void ParallelForEach(int count) - { - int sum = 0, expected = 0; - var items = new List(); - for (int i = 0; i < count; i++) { - items.Add(i); - expected += i; - } - Parallel.ForEach(items, (i) => { sum += i; }); - Assert.Equal(expected, sum); - } - } -} \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/TypedArrayTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/TypedArrayTests.cs deleted file mode 100644 index 9df7fd8719bccf..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/TypedArrayTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 System.Runtime.InteropServices.JavaScript.Tests -{ - public static class TypedArrayTests - { - private static Function _objectPrototype; - - public static IEnumerable Object_Prototype() - { - _objectPrototype ??= new Function("return Object.prototype.toString;"); - yield return new object[] { _objectPrototype.Call() }; - } - - [Theory] - [MemberData(nameof(Object_Prototype))] - public static void Uint8ArrayFrom(Function objectPrototype) - { - var array = new byte[50]; - Uint8Array from = Uint8Array.From(array); - Assert.Equal(50, from.Length); - Assert.Equal("[object Uint8Array]", objectPrototype.Call(from)); - } - - [Theory] - [MemberData(nameof(Object_Prototype))] - public static void Uint8ArrayFromArrayBuffer(Function objectPrototype) - { - Uint8Array from = new Uint8Array(new ArrayBuffer(50)); - Assert.True(from.Length == 50); - Assert.Equal("[object Uint8Array]", objectPrototype.Call(from)); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index ab0e7ac77251ef..a0457f006246c6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -8,10 +8,8 @@ $(WasmXHarnessArgs) --engine-arg=--expose-gc --web-server-use-cop true true - false true $(DefineConstants);FEATURE_WASM_THREADS - $(DefineConstants);DISABLE_LEGACY_JS_INTEROP true @@ -24,12 +22,15 @@ + + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/HttpRequestMessageTest.cs similarity index 77% rename from src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs rename to src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/HttpRequestMessageTest.cs index e9c2f68c3097e7..815dfeb0dcd2df 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/Http/HttpRequestMessageTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/HttpRequestMessageTest.cs @@ -366,89 +366,6 @@ public void ToString_NonDefaultInstanceWithCustomHeaders_DumpAllFields(string ur "}", rm.ToString()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBrowserDomSupported))] - public async Task BlobUri_Marshal_CorrectValues_Browser() - { - Utils.InvokeJS(@" - function typedArrayToURL(typedArray, mimeType) { - return URL.createObjectURL(new Blob([typedArray.buffer], {type: mimeType})) - } - const bytes = new Uint8Array(59); - for(let i = 0; i < 59; i++) { - bytes[i] = 32 + i; - } - const url = typedArrayToURL(bytes, 'text/plain'); - // Calls method with string that will be marshaled as valid URI - App.call_test_method (""InvokeString"", [ url ]); - "); - - var client = new HttpClient (); - Assert.StartsWith ("blob:", HelperMarshal._stringResource); - - HttpRequestMessage rm = new HttpRequestMessage(HttpMethod.Get, new Uri (HelperMarshal._stringResource)); - HttpResponseMessage resp = await client.SendAsync (rm); - Assert.NotNull (resp.Content); - string content = await resp.Content.ReadAsStringAsync(); - Assert.Equal (59, content.Length); - } - - [Fact] - public void BlobStringUri_Marshal_CorrectValues() - { - Utils.InvokeJS(@" - function typedArrayToURL(typedArray, mimeType) { - // URL.createObjectURL does not work outside of browser but since this was actual - // test code from https://developer.mozilla.org/en-US/docs/Web/API/Blob - // left it in to show what this should do if the test code were to actually run - //return URL.createObjectURL(new Blob([typedArray.buffer], {type: mimeType})) - return 'blob:https://mdn.mozillademos.org/ca45b575-6348-4d3e-908a-3dbf3d146ea7'; - } - const bytes = new Uint8Array(59); - for(let i = 0; i < 59; i++) { - bytes[i] = 32 + i; - } - const url = typedArrayToURL(bytes, 'text/plain'); - // Calls method with string that will be converted to a valid Uri - // within the method - App.call_test_method (""SetBlobUrl"", [ url ]); - "); - - var rm = new HttpRequestMessage(HttpMethod.Post, HelperMarshal._blobURL); - - Assert.Equal(HttpMethod.Post, rm.Method); - Assert.Equal(_expectedRequestMessageVersion, rm.Version); - Assert.Null(rm.Content); - Assert.Equal(new Uri("blob:https://mdn.mozillademos.org/ca45b575-6348-4d3e-908a-3dbf3d146ea7"), rm.RequestUri); - } - - [Fact] - public void BlobUri_Marshal_CorrectValues() - { - Utils.InvokeJS(@" - function typedArrayToURL(typedArray, mimeType) { - // URL.createObjectURL does not work outside of browser but since this was actual - // test code from https://developer.mozilla.org/en-US/docs/Web/API/Blob - // left it in to show what this should do if the test code were to actually run - //return URL.createObjectURL(new Blob([typedArray.buffer], {type: mimeType})) - return 'blob:https://mdn.mozillademos.org/ca45b575-6348-4d3e-908a-3dbf3d146ea7'; - } - const bytes = new Uint8Array(59); - for(let i = 0; i < 59; i++) { - bytes[i] = 32 + i; - } - const url = typedArrayToURL(bytes, 'text/plain'); - // Calls method with string that will be marshaled as valid URI - App.call_test_method (""SetBlobAsUri"", [ url ]); - "); - - var rm = new HttpRequestMessage(HttpMethod.Post, HelperMarshal._blobURI); - - Assert.Equal(HttpMethod.Post, rm.Method); - Assert.Equal(_expectedRequestMessageVersion, rm.Version); - Assert.Null(rm.Content); - Assert.Equal(new Uri("blob:https://mdn.mozillademos.org/ca45b575-6348-4d3e-908a-3dbf3d146ea7"), rm.RequestUri); - } - #region Helper methods private class MockContent : HttpContent diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs index 8fdcfebf52c410..e7e058e120fb48 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs @@ -68,11 +68,6 @@ public unsafe void GlobalThis() [Fact] public unsafe void DotnetInstance() { -#if !DISABLE_LEGACY_JS_INTEROP - Assert.True(JSHost.DotnetInstance.HasProperty("MONO")); - Assert.Equal("object", JSHost.DotnetInstance.GetTypeOfProperty("MONO")); -#endif - JSHost.DotnetInstance.SetProperty("testBool", true); Assert.Equal("boolean", JSHost.DotnetInstance.GetTypeOfProperty("testBool")); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/TimerTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/TimerTests.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/System/Runtime/InteropServices/JavaScript/TimerTests.cs rename to src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/TimerTests.cs diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/timers.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/timers.mjs similarity index 100% rename from src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Legacy.UnitTests/timers.mjs rename to src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/timers.mjs diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index a822869d3a39a3..043b553b00ab90 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -525,7 +525,6 @@ - diff --git a/src/mono/browser/.gitignore b/src/mono/browser/.gitignore index 6047d67b80823f..6177029d9f9e16 100644 --- a/src/mono/browser/.gitignore +++ b/src/mono/browser/.gitignore @@ -3,4 +3,3 @@ emsdk runtime/dotnet.d.ts.sha256 -runtime/dotnet-legacy.d.ts.sha256 diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 9b962a7efabe4d..3d8cc6b3306c98 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -26,7 +26,6 @@ $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'browser-wasm-threads', 'native', 'lib')) true true - true true false false @@ -393,7 +392,6 @@ $(CMakeBuildRuntimeConfigureCmd) -DCONFIGURATION_INTERPSIMDTABLES_LIB="nosimd" $(CMakeBuildRuntimeConfigureCmd) -DDISABLE_THREADS=0 $(CMakeBuildRuntimeConfigureCmd) -DENABLE_JS_INTEROP_BY_VALUE=1 - $(CMakeBuildRuntimeConfigureCmd) -DDISABLE_LEGACY_JS_INTEROP=1 $(CMakeBuildRuntimeConfigureCmd) $(CMakeConfigurationEmsdkPath) call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && $(CMakeBuildRuntimeConfigureCmd) @@ -405,8 +403,6 @@ bash -c 'source $(EMSDK_PATH)/emsdk_env.sh 2>&1 && $(CMakeBuildRuntimeCmd)' - <_CmakeEnvironmentVariable Include="DISABLE_LEGACY_JS_INTEROP=1" Condition="'$(WasmEnableLegacyJsInterop)' == 'false'"/> - <_CmakeEnvironmentVariable Include="DISABLE_LEGACY_JS_INTEROP=0" Condition="'$(WasmEnableLegacyJsInterop)' != 'false'"/> <_CmakeEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE=1" Condition="'$(WasmEnableJsInteropByValue)' != 'false'"/> <_CmakeEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE=0" Condition="'$(WasmEnableJsInteropByValue)' == 'false'"/> <_CmakeEnvironmentVariable Include="WASM_ENABLE_SIMD=1" Condition="'$(WasmEnableSIMD)' != 'false'" /> @@ -480,7 +476,6 @@ $(NativeBinDir)dotnet.runtime.js.map; $(NativeBinDir)dotnet.native.js; $(NativeBinDir)dotnet.d.ts; - $(NativeBinDir)dotnet-legacy.d.ts; $(NativeBinDir)package.json; $(NativeBinDir)dotnet.native.wasm" DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)" @@ -529,7 +524,7 @@ <_RollupInputs Include="$(BrowserProjectRoot)runtime/**/*.ts" - Exclude="$(BrowserProjectRoot)runtime/dotnet.d.ts;$(BrowserProjectRoot)runtime/dotnet-legacy.d.ts;$(BrowserProjectRoot)runtime/diagnostics-mock.d.ts;$(BrowserProjectRoot)runtime/node_modules/**/*.ts" /> + Exclude="$(BrowserProjectRoot)runtime/dotnet.d.ts;$(BrowserProjectRoot)runtime/diagnostics-mock.d.ts;$(BrowserProjectRoot)runtime/node_modules/**/*.ts" /> <_RollupInputs Include="$(BrowserProjectRoot)runtime/**/tsconfig.*" Exclude="$(BrowserProjectRoot)runtime/node_modules/**/tsconfig.*" /> <_RollupInputs Include="$(BrowserProjectRoot)runtime/workers/**/*.js"/> @@ -549,8 +544,6 @@ <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_SIMD:0" Condition="'$(WasmEnableSIMD)' == 'false'" /> <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_EH:1" Condition="'$(WasmEnableExceptionHandling)' != 'false'" /> <_MonoRollupEnvironmentVariable Include="WASM_ENABLE_EH:0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" /> - <_MonoRollupEnvironmentVariable Include="DISABLE_LEGACY_JS_INTEROP:1" Condition="'$(WasmEnableLegacyJsInterop)' == 'false'" /> - <_MonoRollupEnvironmentVariable Include="DISABLE_LEGACY_JS_INTEROP:0" Condition="'$(WasmEnableLegacyJsInterop)' != 'false'" /> <_MonoRollupEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE:1" Condition="'$(WasmEnableJsInteropByValue)' == 'true'" /> <_MonoRollupEnvironmentVariable Include="ENABLE_JS_INTEROP_BY_VALUE:0" Condition="'$(WasmEnableJsInteropByValue)' != 'true'" /> <_MonoRollupEnvironmentVariable Include="MonoDiagnosticsMock:$(MonoDiagnosticsMock)" /> diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index 9dd40ec65cc642..edba838ff2bd0f 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -28,7 +28,6 @@ _WasmGenerateRunV8Script; - true true false @@ -48,7 +47,6 @@ <_ExtraTrimmerArgs Condition="'$(WasmEnableSIMD)' == 'true' and '$(RunAOTCompilation)' == 'true'">$(_ExtraTrimmerArgs) --substitutions "$(MSBuildThisFileDirectory)ILLink.Substitutions.WasmIntrinsics.xml" <_ExtraTrimmerArgs Condition="'$(WasmEnableSIMD)' != 'true'">$(_ExtraTrimmerArgs) --substitutions "$(MSBuildThisFileDirectory)ILLink.Substitutions.NoWasmIntrinsics.xml" - <_ExtraTrimmerArgs Condition="'$(WasmEnableLegacyJsInterop)' == 'false'">$(_ExtraTrimmerArgs) --substitutions "$(MSBuildThisFileDirectory)ILLink.Substitutions.LegacyJsInterop.xml" true emcc @@ -64,7 +62,6 @@ - <_BoolPropertiesThatTriggerRelinking Include="WasmEnableLegacyJsInterop" DefaultValueInRuntimePack="true" /> <_BoolPropertiesThatTriggerRelinking Include="WasmEnableSIMD" DefaultValueInRuntimePack="true" /> <_BoolPropertiesThatTriggerRelinking Include="WasmEnableExceptionHandling" DefaultValueInRuntimePack="true" /> <_BoolPropertiesThatTriggerRelinking Include="WasmNativeStrip" DefaultValueInRuntimePack="true" /> @@ -278,7 +275,6 @@ $(EmccInitialHeapSize) 5MB false - true $(EmscriptenUpstreamEmscriptenPath)emcc @@ -310,7 +306,6 @@ <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DENABLE_AOT_PROFILER=1" Condition="$(WasmProfilers.Contains('aot'))" /> <_EmccCFlags Include="-DENABLE_BROWSER_PROFILER=1" Condition="$(WasmProfilers.Contains('browser'))" /> - <_EmccCFlags Include="-DDISABLE_LEGACY_JS_INTEROP=1" Condition="'$(WasmEnableLegacyJsInterop)' == 'false'" /> <_EmccCFlags Include="-DENABLE_JS_INTEROP_BY_VALUE=1" Condition="'$(WasmEnableJsInteropByValue)' == 'true'" /> <_EmccCFlags Include="-DGEN_PINVOKE=1" /> @@ -356,8 +351,6 @@ - - diff --git a/src/mono/browser/build/ILLink.Substitutions.LegacyJsInterop.xml b/src/mono/browser/build/ILLink.Substitutions.LegacyJsInterop.xml deleted file mode 100644 index 523a1d947c5f16..00000000000000 --- a/src/mono/browser/build/ILLink.Substitutions.LegacyJsInterop.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/mono/browser/build/README.md b/src/mono/browser/build/README.md index e1565c8359fe5d..495e8020b76a5a 100644 --- a/src/mono/browser/build/README.md +++ b/src/mono/browser/build/README.md @@ -32,8 +32,6 @@ Implementation: - *after* any of the wasm build targets, use `AfterTargets="WasmBuildApp"` on that target - Avoid depending on this target, because it is available only when the workload is installed. Use `$(WasmNativeWorkload)` to check if it is installed. -- When `Module.disableDotnet6Compatibility` is set it would not pollute global namespace. - ## `Publish` Implementation: diff --git a/src/mono/browser/runtime/CMakeLists.txt b/src/mono/browser/runtime/CMakeLists.txt index 64cdb841a349dc..60570d6deafd60 100644 --- a/src/mono/browser/runtime/CMakeLists.txt +++ b/src/mono/browser/runtime/CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.20) project(mono-wasm-runtime C) option(DISABLE_THREADS "defined if the build does NOT support multithreading" ON) -option(DISABLE_LEGACY_JS_INTEROP "defined if the build does not support legacy JavaScript interop" OFF) option(ENABLE_JS_INTEROP_BY_VALUE "defined when JS interop without pointers to managed objects" OFF) set(CMAKE_EXECUTABLE_SUFFIX ".js") diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c index 4b52eb4badf22c..9dc2efa3de77e5 100644 --- a/src/mono/browser/runtime/corebindings.c +++ b/src/mono/browser/runtime/corebindings.c @@ -22,21 +22,6 @@ extern void mono_wasm_resolve_or_reject_promise(void *data); typedef void (*background_job_cb)(void); -#ifndef DISABLE_LEGACY_JS_INTEROP -extern void mono_wasm_invoke_js_with_args_ref (int js_handle, MonoString **method, MonoArray **args, int *is_exception, MonoObject **result); -extern void mono_wasm_get_object_property_ref (int js_handle, MonoString **propertyName, int *is_exception, MonoObject **result); -extern void mono_wasm_set_object_property_ref (int js_handle, MonoString **propertyName, MonoObject **value, int createIfNotExist, int hasOwnProperty, int *is_exception, MonoObject **result); -extern void mono_wasm_get_by_index_ref (int js_handle, int property_index, int *is_exception, MonoObject **result); -extern void mono_wasm_set_by_index_ref (int js_handle, int property_index, MonoObject **value, int *is_exception, MonoObject **result); -extern void mono_wasm_get_global_object_ref (MonoString **global_name, int *is_exception, MonoObject **result); -extern void mono_wasm_typed_array_to_array_ref (int js_handle, int *is_exception, MonoObject **result); -extern void mono_wasm_create_cs_owned_object_ref (MonoString **core_name, MonoArray **args, int *is_exception, MonoObject** result); -extern void mono_wasm_typed_array_from_ref (int ptr, int begin, int end, int bytes_per_element, int type, int *is_exception, MonoObject** result); - -// Blazor specific custom routines - see dotnet_support.js for backing code -extern void* mono_wasm_invoke_js_blazor (MonoString **exceptionMessage, void *callInfo, void* arg0, void* arg1, void* arg2); -#endif /* DISABLE_LEGACY_JS_INTEROP */ - #ifndef DISABLE_THREADS extern void mono_wasm_install_js_worker_interop (int context_gc_handle); extern void mono_wasm_uninstall_js_worker_interop (); @@ -87,21 +72,6 @@ void bindings_initialize_internals (void) mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_js_function); #endif /* DISABLE_THREADS */ -#ifndef DISABLE_LEGACY_JS_INTEROP - // legacy - mono_add_internal_call ("Interop/Runtime::InvokeJSWithArgsRef", mono_wasm_invoke_js_with_args_ref); - mono_add_internal_call ("Interop/Runtime::GetObjectPropertyRef", mono_wasm_get_object_property_ref); - mono_add_internal_call ("Interop/Runtime::SetObjectPropertyRef", mono_wasm_set_object_property_ref); - mono_add_internal_call ("Interop/Runtime::GetByIndexRef", mono_wasm_get_by_index_ref); - mono_add_internal_call ("Interop/Runtime::SetByIndexRef", mono_wasm_set_by_index_ref); - mono_add_internal_call ("Interop/Runtime::GetGlobalObjectRef", mono_wasm_get_global_object_ref); - mono_add_internal_call ("Interop/Runtime::TypedArrayToArrayRef", mono_wasm_typed_array_to_array_ref); - mono_add_internal_call ("Interop/Runtime::CreateCSOwnedObjectRef", mono_wasm_create_cs_owned_object_ref); - mono_add_internal_call ("Interop/Runtime::TypedArrayFromRef", mono_wasm_typed_array_from_ref); - - // Blazor specific custom routines - see dotnet_support.js for backing code - mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJS", mono_wasm_invoke_js_blazor); -#endif /* DISABLE_LEGACY_JS_INTEROP */ mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant); mono_add_internal_call ("Interop/JsGlobalization::ChangeCase", mono_wasm_change_case); mono_add_internal_call ("Interop/JsGlobalization::CompareString", mono_wasm_compare_string); diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 0a3f2e0894cd1d..180bbd5ccc2c1b 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -2,36 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. import MonoWasmThreads from "consts:monoWasmThreads"; -import WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop"; import type { - MonoArray, MonoAssembly, MonoClass, + MonoAssembly, MonoClass, MonoMethod, MonoObject, MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments } from "./types/internal"; import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten"; -import { linkerDisableLegacyJsInterop, linkerEnableAotProfiler, linkerEnableBrowserProfiler, Module } from "./globals"; +import { linkerEnableAotProfiler, linkerEnableBrowserProfiler, Module } from "./globals"; import { mono_log_error } from "./logging"; import { mono_assert } from "./globals"; type SigLine = [lazyOrSkip: boolean | (() => boolean), name: string, returnType: string | null, argTypes?: string[], opts?: any]; -const legacy_interop_cwraps: SigLine[] = WasmEnableLegacyJsInterop ? [ - [true, "mono_wasm_array_get_ref", "void", ["number", "number", "number"]], - [true, "mono_wasm_obj_array_new_ref", "void", ["number", "number"]], - [true, "mono_wasm_obj_array_set_ref", "void", ["number", "number", "number"]], - [true, "mono_wasm_try_unbox_primitive_and_get_type_ref", "number", ["number", "number", "number"]], - [true, "mono_wasm_box_primitive_ref", "void", ["number", "number", "number", "number"]], - [true, "mono_wasm_string_array_new_ref", "void", ["number", "number"]], - [true, "mono_wasm_typed_array_new_ref", "void", ["number", "number", "number", "number", "number"]], - [true, "mono_wasm_get_delegate_invoke_ref", "number", ["number"]], - [true, "mono_wasm_get_type_name", "string", ["number"]], - [true, "mono_wasm_get_type_aqn", "string", ["number"]], - [true, "mono_wasm_obj_array_new", "number", ["number"]], - [true, "mono_wasm_obj_array_set", "void", ["number", "number", "number"]], - [true, "mono_wasm_array_length_ref", "number", ["number"]], -] : []; - const threading_cwraps: SigLine[] = MonoWasmThreads ? [ // MONO.diagnostics [true, "mono_wasm_event_pipe_enable", "bool", ["string", "number", "number", "string", "bool", "number"]], @@ -46,7 +29,6 @@ const threading_cwraps: SigLine[] = MonoWasmThreads ? [ // when the method is assigned/cached at usage, instead of being invoked directly from cwraps, it can't be marked lazy, because it would be re-bound on each call const fn_signatures: SigLine[] = [ - // MONO [true, "mono_wasm_register_root", "number", ["number", "number", "string"]], [true, "mono_wasm_deregister_root", null, ["number"]], [true, "mono_wasm_string_get_data_ref", null, ["number", "number", "number", "number"]], @@ -64,7 +46,6 @@ const fn_signatures: SigLine[] = [ [false, "mono_wasm_load_runtime", null, ["string", "number"]], [true, "mono_wasm_change_debugger_log_level", "void", ["number"]], - // BINDING [true, "mono_wasm_get_corlib", "number", []], [true, "mono_wasm_assembly_load", "number", ["string"]], [true, "mono_wasm_assembly_find_class", "number", ["number", "string", "string"]], @@ -76,7 +57,6 @@ const fn_signatures: SigLine[] = [ [true, "mono_wasm_assembly_get_entry_point", "number", ["number", "number"]], [true, "mono_wasm_class_get_type", "number", ["number"]], - //INTERNAL [false, "mono_wasm_exit", "void", ["number"]], [false, "mono_wasm_abort", "void", []], [true, "mono_wasm_getenv", "number", ["string"]], @@ -155,26 +135,8 @@ const fn_signatures: SigLine[] = [ [true, "mono_interp_pgo_save_table", "number", ["number", "number"]], ...threading_cwraps, - ...legacy_interop_cwraps, ]; -export interface t_LegacyCwraps { - // legacy interop - mono_wasm_array_get_ref(array: MonoObjectRef, idx: number, result: MonoObjectRef): void; - mono_wasm_obj_array_new_ref(size: number, result: MonoObjectRef): void; - mono_wasm_obj_array_set_ref(array: MonoObjectRef, idx: number, obj: MonoObjectRef): void; - mono_wasm_try_unbox_primitive_and_get_type_ref(obj: MonoObjectRef, buffer: VoidPtr, buffer_size: number): number; - mono_wasm_box_primitive_ref(klass: MonoClass, value: VoidPtr, value_size: number, result: MonoObjectRef): void; - mono_wasm_string_array_new_ref(size: number, result: MonoObjectRef): void; - mono_wasm_typed_array_new_ref(arr: VoidPtr, length: number, size: number, type: number, result: MonoObjectRef): void; - mono_wasm_get_delegate_invoke_ref(delegate: MonoObjectRef): MonoMethod; - mono_wasm_get_type_name(ty: MonoType): string; - mono_wasm_get_type_aqn(ty: MonoType): string; - mono_wasm_obj_array_new(size: number): MonoArray; - mono_wasm_obj_array_set(array: MonoArray, idx: number, obj: MonoObject): void; - mono_wasm_array_length_ref(array: MonoObjectRef): number; -} - export interface t_ThreadingCwraps { // MONO.diagnostics mono_wasm_event_pipe_enable(outputPath: string | null, stream: VoidPtr, bufferSizeInMB: number, providers: string, rundownRequested: boolean, outSessionId: VoidPtr): boolean; @@ -193,7 +155,6 @@ export interface t_ProfilerCwraps { } export interface t_Cwraps { - // MONO mono_wasm_register_root(start: VoidPtr, size: number, name: string): number; mono_wasm_deregister_root(addr: VoidPtr): void; mono_wasm_string_get_data_ref(stringRef: MonoStringRef, outChars: CharPtrPtr, outLengthBytes: Int32Ptr, outIsInterned: Int32Ptr): void; @@ -211,7 +172,6 @@ export interface t_Cwraps { mono_wasm_load_runtime(unused: string, debugLevel: number): void; mono_wasm_change_debugger_log_level(value: number): void; - // BINDING mono_wasm_get_corlib(): MonoAssembly; mono_wasm_assembly_load(name: string): MonoAssembly; mono_wasm_assembly_find_class(assembly: MonoAssembly, namespace: string, name: string): MonoClass; @@ -222,7 +182,6 @@ export interface t_Cwraps { mono_wasm_assembly_get_entry_point(assembly: MonoAssembly, idx: number): MonoMethod; mono_wasm_intern_string_ref(strRef: MonoStringRef): void; - //INTERNAL mono_wasm_exit(exit_code: number): void; mono_wasm_abort(): void; mono_wasm_getenv(name: string): CharPtr; @@ -308,7 +267,6 @@ export interface t_Cwraps { const wrapped_c_functions: t_Cwraps = {}; export default wrapped_c_functions; -export const legacy_c_functions: t_LegacyCwraps & t_Cwraps = wrapped_c_functions as any; export const threads_c_functions: t_ThreadingCwraps & t_Cwraps = wrapped_c_functions as any; export const profiler_c_functions: t_ProfilerCwraps & t_Cwraps = wrapped_c_functions as any; @@ -353,8 +311,7 @@ function cwrap(name: string, returnType: string | null, argTypes: string[] | und } export function init_c_exports(): void { - const lfns = WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop ? legacy_interop_cwraps : []; - const fns = [...fn_signatures, ...lfns]; + const fns = [...fn_signatures]; for (const sig of fns) { const wf: any = wrapped_c_functions; const [lazyOrSkip, name, returnType, argTypes, opts] = sig; diff --git a/src/mono/browser/runtime/dotnet-legacy.d.ts b/src/mono/browser/runtime/dotnet-legacy.d.ts deleted file mode 100644 index ede3f4a2231c54..00000000000000 --- a/src/mono/browser/runtime/dotnet-legacy.d.ts +++ /dev/null @@ -1,295 +0,0 @@ -//! Licensed to the .NET Foundation under one or more agreements. -//! The .NET Foundation licenses this file to you under the MIT license. -//! -//! This is generated file, see src/mono/wasm/runtime/rollup.config.js - -//! This is not considered public API with backward compatibility guarantees. - -declare interface ManagedPointer { - __brandManagedPointer: "ManagedPointer"; -} -declare interface NativePointer { - __brandNativePointer: "NativePointer"; -} -declare interface VoidPtr extends NativePointer { - __brand: "VoidPtr"; -} - -interface MonoObject extends ManagedPointer { - __brandMonoObject: "MonoObject"; -} -interface MonoString extends MonoObject { - __brand: "MonoString"; -} -interface MonoArray extends MonoObject { - __brand: "MonoArray"; -} -interface MonoObjectRef extends ManagedPointer { - __brandMonoObjectRef: "MonoObjectRef"; -} -type MemOffset = number | VoidPtr | NativePointer | ManagedPointer; -type NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer; -interface WasmRoot { - get_address(): MonoObjectRef; - get_address_32(): number; - get address(): MonoObjectRef; - get(): T; - set(value: T): T; - get value(): T; - set value(value: T); - copy_from_address(source: MonoObjectRef): void; - copy_to_address(destination: MonoObjectRef): void; - copy_from(source: WasmRoot): void; - copy_to(destination: WasmRoot): void; - valueOf(): T; - clear(): void; - release(): void; - toString(): string; -} -interface WasmRootBuffer { - get_address(index: number): MonoObjectRef; - get_address_32(index: number): number; - get(index: number): ManagedPointer; - set(index: number, value: ManagedPointer): ManagedPointer; - copy_value_from_address(index: number, sourceAddress: MonoObjectRef): void; - clear(): void; - release(): void; - toString(): string; -} - -/** - * @deprecated Please use methods in top level API object instead - */ -type BINDINGType = { - /** - * @deprecated Please use [JSExportAttribute] instead - */ - bind_static_method: (fqn: string, signature?: string) => Function; - /** - * @deprecated Please use runMain() instead - */ - call_assembly_entry_point: (assembly: string, args?: any[], signature?: string) => number; - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_new: (size: number) => MonoArray; - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void; - /** - * @deprecated Not GC or thread safe - */ - js_string_to_mono_string: (string: string) => MonoString; - /** - * @deprecated Not GC or thread safe - */ - js_typed_array_to_array: (js_obj: any) => MonoArray; - /** - * @deprecated Not GC or thread safe - */ - mono_array_to_js_array: (mono_array: MonoArray) => any[] | null; - /** - * @deprecated Not GC or thread safe - */ - js_to_mono_obj: (js_obj: any) => MonoObject; - /** - * @deprecated Not GC or thread safe - */ - conv_string: (mono_obj: MonoString) => string | null; - /** - * @deprecated Not GC or thread safe - */ - unbox_mono_obj: (mono_obj: MonoObject) => any; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_obj_array_new_ref: (size: number, result: MonoObjectRef) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_obj_array_set_ref: (array: MonoObjectRef, idx: number, obj: MonoObjectRef) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - js_string_to_mono_string_root: (string: string, result: WasmRoot) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - js_typed_array_to_array_root: (js_obj: any, result: WasmRoot) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - js_to_mono_obj_root: (js_obj: any, result: WasmRoot, should_add_in_flight: boolean) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - conv_string_root: (root: WasmRoot) => string | null; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - unbox_mono_obj_root: (root: WasmRoot) => any; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_array_root_to_js_array: (arrayRoot: WasmRoot) => any[] | null; -}; -/** - * @deprecated Please use methods in top level API object instead - */ -type MONOType = { - /** - * @deprecated Please use setEnvironmentVariable() instead - */ - mono_wasm_setenv: (name: string, value: string) => void; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_load_bytes_into_heap: (bytes: Uint8Array) => VoidPtr; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_load_icu_data: (offset: VoidPtr) => boolean; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_runtime_ready: () => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_new_root_buffer: (capacity: number, name?: string) => WasmRootBuffer; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_new_root: (value?: T | undefined) => WasmRoot; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_new_external_root: (address: VoidPtr | MonoObjectRef) => WasmRoot; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_release_roots: (...args: WasmRoot[]) => void; - /** - * @deprecated Please use runMain instead - */ - mono_run_main: (main_assembly_name: string, args: string[]) => Promise; - /** - * @deprecated Please use runMainAndExit instead - */ - mono_run_main_and_exit: (main_assembly_name: string, args: string[]) => Promise; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_load_runtime: (unused: string, debugLevel: number) => void; - /** - * @deprecated Please use getConfig() instead - */ - config: any; - /** - * @deprecated Please use config.assets instead - */ - loaded_files: string[]; - /** - * @deprecated Please use setHeapB32 - */ - setB32: (offset: MemOffset, value: number | boolean) => void; - /** - * @deprecated Please use setHeapI8 - */ - setI8: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI16 - */ - setI16: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI32 - */ - setI32: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI52 - */ - setI52: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapU52 - */ - setU52: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI64Big - */ - setI64Big: (offset: MemOffset, value: bigint) => void; - /** - * @deprecated Please use setHeapU8 - */ - setU8: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapU16 - */ - setU16: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapU32 - */ - setU32: (offset: MemOffset, value: NumberOrPointer) => void; - /** - * @deprecated Please use setHeapF32 - */ - setF32: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapF64 - */ - setF64: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use getHeapB32 - */ - getB32: (offset: MemOffset) => boolean; - /** - * @deprecated Please use getHeapI8 - */ - getI8: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI16 - */ - getI16: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI32 - */ - getI32: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI52 - */ - getI52: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapU52 - */ - getU52: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI64Big - */ - getI64Big: (offset: MemOffset) => bigint; - /** - * @deprecated Please use getHeapU8 - */ - getU8: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapU16 - */ - getU16: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapU32 - */ - getU32: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapF32 - */ - getF32: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapF64 - */ - getF64: (offset: MemOffset) => number; -}; - -export { BINDINGType, MONOType, MonoArray, MonoObject, MonoString }; diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts index 5dde3a3e265ca1..3670e7c42389d3 100644 --- a/src/mono/browser/runtime/dotnet.d.ts +++ b/src/mono/browser/runtime/dotnet.d.ts @@ -18,24 +18,6 @@ declare interface Int32Ptr extends NativePointer { __brand: "Int32Ptr"; } declare interface EmscriptenModule { - /** @deprecated Please use localHeapViewI8() instead.*/ - HEAP8: Int8Array; - /** @deprecated Please use localHeapViewI16() instead.*/ - HEAP16: Int16Array; - /** @deprecated Please use localHeapViewI32() instead. */ - HEAP32: Int32Array; - /** @deprecated Please use localHeapViewI64() instead. */ - HEAP64: BigInt64Array; - /** @deprecated Please use localHeapViewU8() instead. */ - HEAPU8: Uint8Array; - /** @deprecated Please use localHeapViewU16() instead. */ - HEAPU16: Uint16Array; - /** @deprecated Please use localHeapViewU32() instead */ - HEAPU32: Uint32Array; - /** @deprecated Please use localHeapViewF32() instead */ - HEAPF32: Float32Array; - /** @deprecated Please use localHeapViewF64() instead. */ - HEAPF64: Float64Array; _malloc(size: number): VoidPtr; _free(ptr: VoidPtr): void; out(message: string): void; @@ -378,7 +360,6 @@ declare const enum GlobalizationMode { Hybrid = "hybrid" } type DotnetModuleConfig = { - disableDotnet6Compatibility?: boolean; config?: MonoConfig; configSrc?: string; onConfigLoaded?: (config: MonoConfig) => void | Promise; @@ -430,14 +411,6 @@ type APIType = { localHeapViewF64: () => Float64Array; }; type RuntimeAPI = { - /** - * @deprecated Please use API object instead. See also MONOType in dotnet-legacy.d.ts - */ - MONO: any; - /** - * @deprecated Please use API object instead. See also BINDINGType in dotnet-legacy.d.ts - */ - BINDING: any; INTERNAL: any; Module: EmscriptenModule; runtimeId: number; diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index a48e77894018e6..47ca561e7c2400 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -52,64 +52,6 @@ extern void mono_bundled_resources_add_assembly_resource (const char *id, const extern void mono_bundled_resources_add_assembly_symbol_resource (const char *id, const uint8_t *data, uint32_t size, void (*free_func)(void *, void *), void *free_data); extern void mono_bundled_resources_add_satellite_assembly_resource (const char *id, const char *name, const char *culture, const uint8_t *data, uint32_t size, void (*free_func)(void *, void*), void *free_data); -#ifndef DISABLE_LEGACY_JS_INTEROP - -#define MARSHAL_TYPE_NULL 0 -#define MARSHAL_TYPE_INT 1 -#define MARSHAL_TYPE_FP64 2 -#define MARSHAL_TYPE_STRING 3 -#define MARSHAL_TYPE_VT 4 -#define MARSHAL_TYPE_DELEGATE 5 -#define MARSHAL_TYPE_TASK 6 -#define MARSHAL_TYPE_OBJECT 7 -#define MARSHAL_TYPE_BOOL 8 -#define MARSHAL_TYPE_ENUM 9 -#define MARSHAL_TYPE_DATE 20 -#define MARSHAL_TYPE_DATEOFFSET 21 -#define MARSHAL_TYPE_URI 22 -#define MARSHAL_TYPE_SAFEHANDLE 23 - -// typed array marshaling -#define MARSHAL_ARRAY_BYTE 10 -#define MARSHAL_ARRAY_UBYTE 11 -#define MARSHAL_ARRAY_UBYTE_C 12 -#define MARSHAL_ARRAY_SHORT 13 -#define MARSHAL_ARRAY_USHORT 14 -#define MARSHAL_ARRAY_INT 15 -#define MARSHAL_ARRAY_UINT 16 -#define MARSHAL_ARRAY_FLOAT 17 -#define MARSHAL_ARRAY_DOUBLE 18 - -#define MARSHAL_TYPE_FP32 24 -#define MARSHAL_TYPE_UINT32 25 -#define MARSHAL_TYPE_INT64 26 -#define MARSHAL_TYPE_UINT64 27 -#define MARSHAL_TYPE_CHAR 28 -#define MARSHAL_TYPE_STRING_INTERNED 29 -#define MARSHAL_TYPE_VOID 30 -#define MARSHAL_TYPE_POINTER 32 - -// errors -#define MARSHAL_ERROR_BUFFER_TOO_SMALL 512 -#define MARSHAL_ERROR_NULL_CLASS_POINTER 513 -#define MARSHAL_ERROR_NULL_TYPE_POINTER 514 - -static MonoClass* datetime_class; -static MonoClass* datetimeoffset_class; -static MonoClass* uri_class; -static MonoClass* task_class; -static MonoClass* safehandle_class; -static MonoClass* voidtaskresult_class; - -static int resolved_datetime_class = 0, - resolved_datetimeoffset_class = 0, - resolved_uri_class = 0, - resolved_task_class = 0, - resolved_safehandle_class = 0, - resolved_voidtaskresult_class = 0; - -#endif /* DISABLE_LEGACY_JS_INTEROP */ - int mono_string_instance_is_interned (MonoString *str_raw); @@ -421,470 +363,6 @@ mono_wasm_string_from_utf16_ref (const mono_unichar2 * chars, int length, MonoSt MONO_EXIT_GC_UNSAFE; } -#ifndef DISABLE_LEGACY_JS_INTEROP - -static int -class_is_task (MonoClass *klass) -{ - if (!klass) - return 0; - - int result; - MONO_ENTER_GC_UNSAFE; - if (!task_class && !resolved_task_class) { - task_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "Task"); - resolved_task_class = 1; - } - - result = task_class && (klass == task_class || mono_class_is_subclass_of(klass, task_class, 0)); - MONO_EXIT_GC_UNSAFE; - return result; -} - -static MonoClass* -_get_uri_class(MonoException** exc) -{ - MonoAssembly* assembly = mono_wasm_assembly_load ("System"); - if (!assembly) - return NULL; - MonoClass* klass = mono_wasm_assembly_find_class(assembly, "System", "Uri"); - return klass; -} - -static void -_ensure_classes_resolved (void) -{ - MONO_ENTER_GC_UNSAFE; - if (!datetime_class && !resolved_datetime_class) { - datetime_class = mono_class_from_name (mono_get_corlib(), "System", "DateTime"); - resolved_datetime_class = 1; - } - if (!datetimeoffset_class && !resolved_datetimeoffset_class) { - datetimeoffset_class = mono_class_from_name (mono_get_corlib(), "System", "DateTimeOffset"); - resolved_datetimeoffset_class = 1; - } - if (!uri_class && !resolved_uri_class) { - PVOLATILE(MonoException) exc = NULL; - uri_class = _get_uri_class((MonoException **)&exc); - resolved_uri_class = 1; - } - if (!safehandle_class && !resolved_safehandle_class) { - safehandle_class = mono_class_from_name (mono_get_corlib(), "System.Runtime.InteropServices", "SafeHandle"); - resolved_safehandle_class = 1; - } - if (!voidtaskresult_class && !resolved_voidtaskresult_class) { - voidtaskresult_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "VoidTaskResult"); - resolved_voidtaskresult_class = 1; - } - MONO_EXIT_GC_UNSAFE; -} - -// This must be run inside a GC unsafe region -static int -_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type) -{ - switch (mono_type) { - // case MONO_TYPE_CHAR: prob should be done not as a number? - case MONO_TYPE_VOID: - return MARSHAL_TYPE_VOID; - case MONO_TYPE_BOOLEAN: - return MARSHAL_TYPE_BOOL; - case MONO_TYPE_I: // IntPtr - case MONO_TYPE_U: // UIntPtr - case MONO_TYPE_PTR: - return MARSHAL_TYPE_POINTER; - case MONO_TYPE_I1: - case MONO_TYPE_I2: - case MONO_TYPE_I4: - return MARSHAL_TYPE_INT; - case MONO_TYPE_CHAR: - return MARSHAL_TYPE_CHAR; - case MONO_TYPE_U1: - case MONO_TYPE_U2: - case MONO_TYPE_U4: // The distinction between this and signed int is - // important due to how numbers work in JavaScript - return MARSHAL_TYPE_UINT32; - case MONO_TYPE_I8: - return MARSHAL_TYPE_INT64; - case MONO_TYPE_U8: - return MARSHAL_TYPE_UINT64; - case MONO_TYPE_R4: - return MARSHAL_TYPE_FP32; - case MONO_TYPE_R8: - return MARSHAL_TYPE_FP64; - case MONO_TYPE_STRING: - return MARSHAL_TYPE_STRING; - case MONO_TYPE_SZARRAY: { // simple zero based one-dim-array - if (klass) { - MonoClass *eklass = mono_class_get_element_class (klass); - MonoType *etype = mono_class_get_type (eklass); - - switch (mono_type_get_type (etype)) { - case MONO_TYPE_U1: - return MARSHAL_ARRAY_UBYTE; - case MONO_TYPE_I1: - return MARSHAL_ARRAY_BYTE; - case MONO_TYPE_U2: - return MARSHAL_ARRAY_USHORT; - case MONO_TYPE_I2: - return MARSHAL_ARRAY_SHORT; - case MONO_TYPE_U4: - return MARSHAL_ARRAY_UINT; - case MONO_TYPE_I4: - return MARSHAL_ARRAY_INT; - case MONO_TYPE_R4: - return MARSHAL_ARRAY_FLOAT; - case MONO_TYPE_R8: - return MARSHAL_ARRAY_DOUBLE; - default: - return MARSHAL_TYPE_OBJECT; - } - } else { - return MARSHAL_TYPE_OBJECT; - } - } - default: - _ensure_classes_resolved (); - - if (klass) { - if (klass == datetime_class) - return MARSHAL_TYPE_DATE; - if (klass == datetimeoffset_class) - return MARSHAL_TYPE_DATEOFFSET; - if (uri_class && mono_class_is_assignable_from(uri_class, klass)) - return MARSHAL_TYPE_URI; - if (klass == voidtaskresult_class) - return MARSHAL_TYPE_VOID; - if (mono_class_is_enum (klass)) - return MARSHAL_TYPE_ENUM; - if (type && !mono_type_is_reference (type)) //vt - return MARSHAL_TYPE_VT; - if (mono_class_is_delegate (klass)) - return MARSHAL_TYPE_DELEGATE; - if (class_is_task(klass)) - return MARSHAL_TYPE_TASK; - if (safehandle_class && (klass == safehandle_class || mono_class_is_subclass_of(klass, safehandle_class, 0))) - return MARSHAL_TYPE_SAFEHANDLE; - } - - return MARSHAL_TYPE_OBJECT; - } -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_typed_array_new_ref (char *arr, int length, int size, int type, PPVOLATILE(MonoArray) result) -{ - MONO_ENTER_GC_UNSAFE; - MonoClass * typeClass = mono_get_byte_class(); // default is Byte - switch (type) { - case MARSHAL_ARRAY_BYTE: - typeClass = mono_get_sbyte_class(); - break; - case MARSHAL_ARRAY_SHORT: - typeClass = mono_get_int16_class(); - break; - case MARSHAL_ARRAY_USHORT: - typeClass = mono_get_uint16_class(); - break; - case MARSHAL_ARRAY_INT: - typeClass = mono_get_int32_class(); - break; - case MARSHAL_ARRAY_UINT: - typeClass = mono_get_uint32_class(); - break; - case MARSHAL_ARRAY_FLOAT: - typeClass = mono_get_single_class(); - break; - case MARSHAL_ARRAY_DOUBLE: - typeClass = mono_get_double_class(); - break; - case MARSHAL_ARRAY_UBYTE: - case MARSHAL_ARRAY_UBYTE_C: - typeClass = mono_get_byte_class(); - break; - default: - printf ("Invalid marshal type %d in mono_wasm_typed_array_new", type); - abort(); - } - - PVOLATILE(MonoArray) buffer; - - buffer = mono_array_new (mono_get_root_domain(), typeClass, length); - memcpy(mono_array_addr_with_size(buffer, sizeof(char), 0), arr, length * size); - - store_volatile((PPVOLATILE(MonoObject))result, (MonoObject *)buffer); - MONO_EXIT_GC_UNSAFE; -} - -EMSCRIPTEN_KEEPALIVE MonoMethod* -mono_wasm_get_delegate_invoke_ref (MonoObject **delegate) -{ - MonoMethod * result; - MONO_ENTER_GC_UNSAFE; - result = mono_get_delegate_invoke(mono_object_get_class (*delegate)); - MONO_EXIT_GC_UNSAFE; - return result; -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_box_primitive_ref (MonoClass *klass, void *value, int value_size, PPVOLATILE(MonoObject) result) -{ - assert (klass); - - MONO_ENTER_GC_UNSAFE; - MonoType *type = mono_class_get_type (klass); - int alignment; - - if (mono_type_size (type, &alignment) <= value_size) - // TODO: use mono_value_box_checked and propagate error out - store_volatile(result, mono_value_box (root_domain, klass, value)); - - MONO_EXIT_GC_UNSAFE; -} - -EMSCRIPTEN_KEEPALIVE char * -mono_wasm_get_type_name (MonoType * typePtr) { - return mono_type_get_name_full (typePtr, MONO_TYPE_NAME_FORMAT_REFLECTION); -} - -EMSCRIPTEN_KEEPALIVE char * -mono_wasm_get_type_aqn (MonoType * typePtr) { - return mono_type_get_name_full (typePtr, MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED); -} - -// this will return bool value if the object is a bool, otherwise it will return -1 or error -EMSCRIPTEN_KEEPALIVE int -mono_wasm_read_as_bool_or_null_unsafe (PVOLATILE(MonoObject) obj) { - - int result = -1; - - MONO_ENTER_GC_UNSAFE; - - MonoClass *klass = mono_object_get_class (obj); - if (!klass) { - goto end; - } - - MonoType *type = mono_class_get_type (klass); - if (!type) { - goto end; - } - - int mono_type = mono_type_get_type (type); - if (MONO_TYPE_BOOLEAN == mono_type) { - result = ((signed char*)mono_object_unbox (obj) == 0 ? 0 : 1); - } - - end: - MONO_EXIT_GC_UNSAFE; - return result; -} - -// This code runs inside a gc unsafe region -static int -_mono_wasm_try_unbox_primitive_and_get_type_ref_impl (PVOLATILE(MonoObject) obj, void *result, int result_capacity) { - void **resultP = result; - int *resultI = result; - uint32_t *resultU = result; - int64_t *resultL = result; - float *resultF = result; - double *resultD = result; - - /* Process obj before calling into the runtime, class_from_name () can invoke managed code */ - MonoClass *klass = mono_object_get_class (obj); - if (!klass) - return MARSHAL_ERROR_NULL_CLASS_POINTER; - - MonoType *type = mono_class_get_type (klass), *original_type = type; - if (!type) - return MARSHAL_ERROR_NULL_TYPE_POINTER; - - if ((klass == mono_get_string_class ()) && - mono_string_instance_is_interned ((MonoString *)obj)) { - *resultL = 0; - *resultP = type; - return MARSHAL_TYPE_STRING_INTERNED; - } - - if (mono_class_is_enum (klass)) - type = mono_type_get_underlying_type (type); - - if (!type) - return MARSHAL_ERROR_NULL_TYPE_POINTER; - - int mono_type = mono_type_get_type (type); - - if (mono_type == MONO_TYPE_GENERICINST) { - // HACK: While the 'any other type' fallback is valid for classes, it will do the - // wrong thing for structs, so we need to make sure the valuetype handler is used - if (mono_type_generic_inst_is_valuetype (type)) - mono_type = MONO_TYPE_VALUETYPE; - } - - // FIXME: We would prefer to unbox once here but it will fail if the value isn't unboxable - - switch (mono_type) { - case MONO_TYPE_I1: - case MONO_TYPE_BOOLEAN: - *resultI = *(signed char*)mono_object_unbox (obj); - break; - case MONO_TYPE_U1: - *resultU = *(unsigned char*)mono_object_unbox (obj); - break; - case MONO_TYPE_I2: - case MONO_TYPE_CHAR: - *resultI = *(short*)mono_object_unbox (obj); - break; - case MONO_TYPE_U2: - *resultU = *(unsigned short*)mono_object_unbox (obj); - break; - case MONO_TYPE_I4: - case MONO_TYPE_I: - *resultI = *(int*)mono_object_unbox (obj); - break; - case MONO_TYPE_U4: - *resultU = *(uint32_t*)mono_object_unbox (obj); - break; - case MONO_TYPE_R4: - *resultF = *(float*)mono_object_unbox (obj); - break; - case MONO_TYPE_R8: - *resultD = *(double*)mono_object_unbox (obj); - break; - case MONO_TYPE_PTR: - *resultU = (uint32_t)(*(void**)mono_object_unbox (obj)); - break; - case MONO_TYPE_I8: - case MONO_TYPE_U8: - // FIXME: At present the javascript side of things can't handle this, - // but there's no reason not to future-proof this API - *resultL = *(int64_t*)mono_object_unbox (obj); - break; - case MONO_TYPE_VALUETYPE: - { - int obj_size = mono_object_get_size (obj), - required_size = (sizeof (int)) + (sizeof (MonoType *)) + obj_size; - - // Check whether this struct has special-case marshaling - // FIXME: Do we need to null out obj before this? - int marshal_type = _marshal_type_from_mono_type (mono_type, klass, original_type); - if (marshal_type != MARSHAL_TYPE_VT) - return marshal_type; - - // Check whether the result buffer is big enough for the struct and padding - if (result_capacity < required_size) - return MARSHAL_ERROR_BUFFER_TOO_SMALL; - - // Store a header before the struct data with the size of the data and its MonoType - *resultP = type; - int * resultSize = (int *)(resultP + 1); - *resultSize = obj_size; - void * resultVoid = (resultP + 2); - void * unboxed = mono_object_unbox (obj); - memcpy (resultVoid, unboxed, obj_size); - return MARSHAL_TYPE_VT; - } - break; - default: - // If we failed to do a fast unboxing, return the original type information so - // that the caller can do a proper, slow unboxing later - // HACK: Store the class pointer into the result buffer so our caller doesn't - // have to call back into the native runtime later to get it - *resultP = type; - int fallbackResultType = _marshal_type_from_mono_type (mono_type, klass, original_type); - assert (fallbackResultType != MARSHAL_TYPE_VT); - return fallbackResultType; - } - - // We successfully performed a fast unboxing here so use the type information - // matching what we unboxed (i.e. an enum's underlying type instead of its type) - int resultType = _marshal_type_from_mono_type (mono_type, klass, type); - assert (resultType != MARSHAL_TYPE_VT); - return resultType; -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_try_unbox_primitive_and_get_type_ref (MonoObject **objRef, void *result, int result_capacity) -{ - if (!result) - return MARSHAL_ERROR_BUFFER_TOO_SMALL; - - int retval; - int *resultI = result; - int64_t *resultL = result; - - if (result_capacity >= sizeof (int64_t)) - *resultL = 0; - else if (result_capacity >= sizeof (int)) - *resultI = 0; - - if (result_capacity < 16) - return MARSHAL_ERROR_BUFFER_TOO_SMALL; - - if (!objRef || !(*objRef)) - return MARSHAL_TYPE_NULL; - - MONO_ENTER_GC_UNSAFE; - retval = _mono_wasm_try_unbox_primitive_and_get_type_ref_impl (*objRef, result, result_capacity); - MONO_EXIT_GC_UNSAFE; - return retval; -} - -EMSCRIPTEN_KEEPALIVE int -mono_wasm_array_length_ref (MonoArray **array) -{ - return mono_array_length (*array); -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_array_get_ref (PPVOLATILE(MonoArray) array, int idx, PPVOLATILE(MonoObject) result) -{ - MONO_ENTER_GC_UNSAFE; - mono_gc_wbarrier_generic_store_atomic((void*)result, mono_array_get ((MonoArray*)*array, MonoObject*, idx)); - MONO_EXIT_GC_UNSAFE; -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_obj_array_new_ref (int size, MonoArray **result) -{ - MONO_ENTER_GC_UNSAFE; - mono_gc_wbarrier_generic_store_atomic(result, (MonoObject *)mono_array_new (root_domain, mono_get_object_class (), size)); - MONO_EXIT_GC_UNSAFE; -} - -// Deprecated -EMSCRIPTEN_KEEPALIVE MonoArray* -mono_wasm_obj_array_new (int size) -{ - PVOLATILE(MonoArray) result = NULL; - mono_wasm_obj_array_new_ref(size, (MonoArray **)&result); - return result; -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_obj_array_set (MonoArray *array, int idx, MonoObject *obj) -{ - mono_array_setref (array, idx, obj); -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_obj_array_set_ref (MonoArray **array, int idx, MonoObject **obj) -{ - MONO_ENTER_GC_UNSAFE; - mono_array_setref (*array, idx, *obj); - MONO_EXIT_GC_UNSAFE; -} - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_string_array_new_ref (int size, MonoArray **result) -{ - MONO_ENTER_GC_UNSAFE; - mono_gc_wbarrier_generic_store_atomic(result, (MonoObject *)mono_array_new (root_domain, mono_get_string_class (), size)); - MONO_EXIT_GC_UNSAFE; -} - -#endif /* DISABLE_LEGACY_JS_INTEROP */ - EMSCRIPTEN_KEEPALIVE int mono_wasm_exec_regression (int verbose_level, char *image) { @@ -1099,3 +577,32 @@ EMSCRIPTEN_KEEPALIVE int mono_wasm_is_zero_page_reserved () { // https://github.com/emscripten-core/emscripten/issues/19389 return (emscripten_stack_get_base() > 512) && (emscripten_stack_get_end() > 512); } + +// this will return bool value if the object is a bool, otherwise it will return -1 or error +// we use it in Blazor's renderBatch as internal only +EMSCRIPTEN_KEEPALIVE int +mono_wasm_read_as_bool_or_null_unsafe (PVOLATILE(MonoObject) obj) { + + int result = -1; + + MONO_ENTER_GC_UNSAFE; + + MonoClass *klass = mono_object_get_class (obj); + if (!klass) { + goto end; + } + + MonoType *type = mono_class_get_type (klass); + if (!type) { + goto end; + } + + int mono_type = mono_type_get_type (type); + if (MONO_TYPE_BOOLEAN == mono_type) { + result = ((signed char*)mono_object_unbox (obj) == 0 ? 0 : 1); + } + + end: + MONO_EXIT_GC_UNSAFE; + return result; +} \ No newline at end of file diff --git a/src/mono/browser/runtime/es6/dotnet.es6.lib.js b/src/mono/browser/runtime/es6/dotnet.es6.lib.js index 78fdc26dfaaac9..0a9236a11d3ba0 100644 --- a/src/mono/browser/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/browser/runtime/es6/dotnet.es6.lib.js @@ -7,7 +7,6 @@ // -- this javascript file is evaluated by emcc during compilation! -- // because we can't pass custom define symbols to acorn optimizer, we use environment variables to pass other build options -const DISABLE_LEGACY_JS_INTEROP = process.env.DISABLE_LEGACY_JS_INTEROP === "1"; const WASM_ENABLE_SIMD = process.env.WASM_ENABLE_SIMD === "1"; const WASM_ENABLE_EH = process.env.WASM_ENABLE_EH === "1"; const ENABLE_BROWSER_PROFILER = process.env.ENABLE_BROWSER_PROFILER === "1"; @@ -83,12 +82,7 @@ function injectDependencies() { createWasmImportStubsFrom(methodIndexByName.mono_wasm_threads_imports); #endif - if (!DISABLE_LEGACY_JS_INTEROP) { - createWasmImportStubsFrom(methodIndexByName.mono_wasm_legacy_interop_imports); - } - DotnetSupportLib["$DOTNET__postset"] = `DOTNET.setup({ ` + - `linkerDisableLegacyJsInterop: ${DISABLE_LEGACY_JS_INTEROP ? "true" : "false"},` + `linkerWasmEnableSIMD: ${WASM_ENABLE_SIMD ? "true" : "false"},` + `linkerWasmEnableEH: ${WASM_ENABLE_EH ? "true" : "false"},` + `linkerEnableAotProfiler: ${ENABLE_AOT_PROFILER ? "true" : "false"}, ` + diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index 34d23af190d220..2f69b78b95fecf 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. import MonoWasmThreads from "consts:monoWasmThreads"; -import WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop"; import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint, mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause } from "./debug"; import { mono_wasm_release_cs_owned_object } from "./gc-handles"; @@ -26,13 +25,6 @@ import { mono_wasm_compare_string, mono_wasm_ends_with, mono_wasm_starts_with, m import { mono_wasm_get_calendar_info } from "./hybrid-globalization/calendar"; import { mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop } from "./pthreads/shared"; -import { - mono_wasm_invoke_js_blazor, mono_wasm_invoke_js_with_args_ref, mono_wasm_get_object_property_ref, mono_wasm_set_object_property_ref, - mono_wasm_get_by_index_ref, mono_wasm_set_by_index_ref, mono_wasm_get_global_object_ref -} from "./net6-legacy/method-calls"; -import { mono_wasm_create_cs_owned_object_ref } from "./net6-legacy/cs-to-js"; -import { mono_wasm_typed_array_to_array_ref } from "./net6-legacy/js-to-cs"; -import { mono_wasm_typed_array_from_ref } from "./net6-legacy/buffers"; import { mono_wasm_get_culture_info } from "./hybrid-globalization/culture-info"; import { mono_wasm_get_first_day_of_week, mono_wasm_get_first_week_of_year } from "./hybrid-globalization/locales"; import { mono_wasm_browser_entropy } from "./crypto"; @@ -57,20 +49,6 @@ export const mono_wasm_threads_imports = !MonoWasmThreads ? [] : [ mono_wasm_invoke_import_sync, ]; -export const mono_wasm_legacy_interop_imports = !WasmEnableLegacyJsInterop ? [] : [ - // corebindings.c - mono_wasm_invoke_js_with_args_ref, - mono_wasm_get_object_property_ref, - mono_wasm_set_object_property_ref, - mono_wasm_get_by_index_ref, - mono_wasm_set_by_index_ref, - mono_wasm_get_global_object_ref, - mono_wasm_create_cs_owned_object_ref, - mono_wasm_typed_array_to_array_ref, - mono_wasm_typed_array_from_ref, - mono_wasm_invoke_js_blazor, -]; - export const mono_wasm_imports = [ // mini-wasm.c mono_wasm_schedule_timer, @@ -127,8 +105,6 @@ const wasmImports: Function[] = [ ...mono_wasm_imports, // threading exports, if threading is enabled ...mono_wasm_threads_imports, - // legacy interop exports, if enabled - ...mono_wasm_legacy_interop_imports ]; export function replace_linker_placeholders(imports: WebAssembly.Imports) { diff --git a/src/mono/browser/runtime/exports-linker.ts b/src/mono/browser/runtime/exports-linker.ts index dd4ed9ec2233be..84e39b446155b8 100644 --- a/src/mono/browser/runtime/exports-linker.ts +++ b/src/mono/browser/runtime/exports-linker.ts @@ -1,14 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { mono_wasm_imports, mono_wasm_legacy_interop_imports, mono_wasm_threads_imports } from "./exports-binding"; +import { mono_wasm_imports, mono_wasm_threads_imports } from "./exports-binding"; import gitHash from "consts:gitHash"; export function export_linker_indexes_as_code(): string { const indexByName: any = { mono_wasm_imports: {}, mono_wasm_threads_imports: {}, - mono_wasm_legacy_interop_imports: {}, }; let idx = 0; for (const wi of mono_wasm_imports) { @@ -19,10 +18,6 @@ export function export_linker_indexes_as_code(): string { indexByName.mono_wasm_threads_imports[wi.name] = idx; idx++; } - for (const wi of mono_wasm_legacy_interop_imports) { - indexByName.mono_wasm_legacy_interop_imports[wi.name] = idx; - idx++; - } return ` var gitHash = "${gitHash}"; var methodIndexByName = ${JSON.stringify(indexByName, null, 2)}; diff --git a/src/mono/browser/runtime/exports.ts b/src/mono/browser/runtime/exports.ts index 35e03cf5db1a44..14032d5134a8d8 100644 --- a/src/mono/browser/runtime/exports.ts +++ b/src/mono/browser/runtime/exports.ts @@ -3,11 +3,10 @@ import ProductVersion from "consts:productVersion"; import BuildConfiguration from "consts:configuration"; -import WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop"; import type { RuntimeAPI } from "./types"; -import { Module, linkerDisableLegacyJsInterop, exportedRuntimeAPI, passEmscriptenInternals, runtimeHelpers, setRuntimeGlobals, } from "./globals"; -import { GlobalObjects, is_nullish } from "./types/internal"; +import { Module, exportedRuntimeAPI, passEmscriptenInternals, runtimeHelpers, setRuntimeGlobals, } from "./globals"; +import { GlobalObjects } from "./types/internal"; import { configureEmscriptenStartup, configureRuntimeStartup, configureWorkerStartup } from "./startup"; import { create_weak_ref } from "./weak-ref"; @@ -15,11 +14,7 @@ import { export_internal } from "./exports-internal"; import { export_api } from "./export-api"; import { initializeReplacements } from "./polyfills"; -// legacy -import { mono_bind_static_method } from "./net6-legacy/method-calls"; -import { export_binding_api, export_internal_api, export_mono_api } from "./net6-legacy/exports-legacy"; -import { initializeLegacyExports } from "./net6-legacy/globals"; -import { mono_log_warn, mono_wasm_stringify_as_error_with_stack } from "./logging"; +import { mono_wasm_stringify_as_error_with_stack } from "./logging"; import { instantiate_asset, instantiate_symbols_asset, instantiate_segmentation_rules_asset } from "./assets"; import { jiterpreter_dump_stats } from "./jiterpreter"; import { forceDisposeProxies } from "./gc-handles"; @@ -29,16 +24,6 @@ function initializeExports(globalObjects: GlobalObjects): RuntimeAPI { const globals = globalObjects; const globalThisAny = globalThis as any; - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { - initializeLegacyExports(globals); - } - - // here we merge methods from the local objects into exported objects - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { - Object.assign(globals.mono, export_mono_api()); - Object.assign(globals.binding, export_binding_api()); - Object.assign(globals.internal, export_internal_api()); - } Object.assign(globals.internal, export_internal()); Object.assign(runtimeHelpers, { stringify_as_error_with_stack: mono_wasm_stringify_as_error_with_stack, @@ -60,60 +45,6 @@ function initializeExports(globalObjects: GlobalObjects): RuntimeAPI { }, ...API, }); - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { - Object.assign(exportedRuntimeAPI, { - MONO: globals.mono, - BINDING: globals.binding, - }); - } - - if (typeof module.disableDotnet6Compatibility === "undefined") { - module.disableDotnet6Compatibility = true; - } - // here we expose objects global namespace for tests and backward compatibility - if (!module.disableDotnet6Compatibility) { - Object.assign(module, exportedRuntimeAPI); - - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { - // backward compatibility - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - module.mono_bind_static_method = (fqn: string, signature: string/*ArgsMarshalString*/): Function => { - mono_log_warn("Module.mono_bind_static_method is obsolete, please use [JSExportAttribute] interop instead"); - return mono_bind_static_method(fqn, signature); - }; - } - - const warnWrap = (name: string, provider: () => any) => { - if (typeof globalThisAny[name] !== "undefined") { - // it already exists in the global namespace - return; - } - let value: any = undefined; - Object.defineProperty(globalThis, name, { - get: () => { - if (is_nullish(value)) { - const stack = (new Error()).stack; - const nextLine = stack ? stack.substr(stack.indexOf("\n", 8) + 1) : ""; - mono_log_warn(`global ${name} is obsolete, please use Module.${name} instead ${nextLine}`); - value = provider(); - } - return value; - } - }); - }; - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { - globalThisAny.MONO = globals.mono; - globalThisAny.BINDING = globals.binding; - globalThisAny.INTERNAL = globals.internal; - } - globalThisAny.Module = module; - - // Blazor back compat - warnWrap("cwrap", () => module.cwrap); - warnWrap("addRunDependency", () => module.addRunDependency); - warnWrap("removeRunDependency", () => module.removeRunDependency); - } // this code makes it possible to find dotnet runtime on a page via global namespace, even when there are multiple runtimes at the same time let list: RuntimeList; diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts index e9c051108dbb60..e8175488d02fc1 100644 --- a/src/mono/browser/runtime/globals.ts +++ b/src/mono/browser/runtime/globals.ts @@ -28,8 +28,7 @@ export let ENVIRONMENT_IS_PTHREAD: boolean; export let exportedRuntimeAPI: RuntimeAPI = null as any; export let runtimeHelpers: RuntimeHelpers = null as any; export let loaderHelpers: LoaderHelpers = null as any; -// this is when we link with workload tools. The consts:wasmEnableLegacyJsInterop is when we compile with rollup. -export let linkerDisableLegacyJsInterop = false; + export let linkerWasmEnableSIMD = true; export let linkerWasmEnableEH = true; export let linkerEnableAotProfiler = false; @@ -39,7 +38,6 @@ export let _runtimeModuleLoaded = false; // please keep it in place also as roll export function passEmscriptenInternals(internals: EmscriptenInternals): void { ENVIRONMENT_IS_PTHREAD = internals.isPThread; - linkerDisableLegacyJsInterop = internals.linkerDisableLegacyJsInterop; linkerWasmEnableSIMD = internals.linkerWasmEnableSIMD; linkerWasmEnableEH = internals.linkerWasmEnableEH; linkerEnableAotProfiler = internals.linkerEnableAotProfiler; diff --git a/src/mono/browser/runtime/loader/globals.ts b/src/mono/browser/runtime/loader/globals.ts index fa07faabf0b6df..dde641c77ff33b 100644 --- a/src/mono/browser/runtime/loader/globals.ts +++ b/src/mono/browser/runtime/loader/globals.ts @@ -70,7 +70,6 @@ export function setLoaderGlobals( }); Object.assign(globalObjects.module, { - disableDotnet6Compatibility: true, config: deep_merge_config(monoConfig, { environmentVariables: {} }), }); Object.assign(runtimeHelpers, { diff --git a/src/mono/browser/runtime/net6-legacy/buffers.ts b/src/mono/browser/runtime/net6-legacy/buffers.ts deleted file mode 100644 index d2b963ee58fde9..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/buffers.ts +++ /dev/null @@ -1,114 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; -import { mono_wasm_new_external_root } from "../roots"; -import { MonoArray, MonoObjectRef, MonoObject } from "../types/internal"; -import { Int32Ptr, TypedArray } from "../types/emscripten"; -import { js_to_mono_obj_root } from "./js-to-cs"; -import { localHeapViewU8 } from "../memory"; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export function mono_wasm_typed_array_from_ref(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const resultRoot = mono_wasm_new_external_root(result_address); - try { - const res = typed_array_from(pinned_array, begin, end, bytes_per_element, type); - // returns JS typed array like Int8Array, to be wraped with JSObject proxy - js_to_mono_obj_root(res, resultRoot, true); - wrap_no_error_root(is_exception); - } catch (exc) { - wrap_error_root(is_exception, String(exc), resultRoot); - } finally { - resultRoot.release(); - } -} - -// Creates a new typed array from pinned array address from pinned_array allocated on the heap to the typed array. -// address of managed pinned array -> copy from heap -> typed array memory -function typed_array_from(pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number, type: number) { - - // typed array - let newTypedArray: TypedArray | null = null; - - switch (type) { - case 5: - newTypedArray = new Int8Array(end - begin); - break; - case 6: - newTypedArray = new Uint8Array(end - begin); - break; - case 7: - newTypedArray = new Int16Array(end - begin); - break; - case 8: - newTypedArray = new Uint16Array(end - begin); - break; - case 9: - newTypedArray = new Int32Array(end - begin); - break; - case 10: - newTypedArray = new Uint32Array(end - begin); - break; - case 13: - newTypedArray = new Float32Array(end - begin); - break; - case 14: - newTypedArray = new Float64Array(end - begin); - break; - case 15: // This is a special case because the typed array is also byte[] - newTypedArray = new Uint8ClampedArray(end - begin); - break; - default: - throw new Error("Unknown array type " + type); - } - - typedarray_copy_from(newTypedArray, pinned_array, begin, end, bytes_per_element); - return newTypedArray; -} - -// Copy the pinned array address from pinned_array allocated on the heap to the typed array. -// address of managed pinned array -> copy from heap -> typed array memory -function typedarray_copy_from(typed_array: TypedArray, pinned_array: MonoArray, begin: number, end: number, bytes_per_element: number) { - - // JavaScript typed arrays are array-like objects and provide a mechanism for accessing - // raw binary data. (...) To achieve maximum flexibility and efficiency, JavaScript typed arrays - // split the implementation into buffers and views. A buffer (implemented by the ArrayBuffer object) - // is an object representing a chunk of data; it has no format to speak of, and offers no - // mechanism for accessing its contents. In order to access the memory contained in a buffer, - // you need to use a view. A view provides a context - that is, a data type, starting offset, - // and number of elements - that turns the data into an actual typed array. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays - if (has_backing_array_buffer(typed_array) && typed_array.BYTES_PER_ELEMENT) { - // Some sanity checks of what is being asked of us - // lets play it safe and throw an error here instead of assuming to much. - // Better safe than sorry later - if (bytes_per_element !== typed_array.BYTES_PER_ELEMENT) - throw new Error("Inconsistent element sizes: TypedArray.BYTES_PER_ELEMENT '" + typed_array.BYTES_PER_ELEMENT + "' sizeof managed element: '" + bytes_per_element + "'"); - - // how much space we have to work with - let num_of_bytes = (end - begin) * bytes_per_element; - // how much typed buffer space are we talking about - const view_bytes = typed_array.length * typed_array.BYTES_PER_ELEMENT; - // only use what is needed. - if (num_of_bytes > view_bytes) - num_of_bytes = view_bytes; - - // Create a new view for mapping - const typedarrayBytes = new Uint8Array(typed_array.buffer, 0, num_of_bytes); - // offset index into the view - const offset = begin * bytes_per_element; - // Set view bytes to value from HEAPU8 - typedarrayBytes.set(localHeapViewU8().subarray(pinned_array + offset, pinned_array + offset + num_of_bytes)); - return num_of_bytes; - } - else { - throw new Error("Object '" + typed_array + "' is not a typed array"); - } -} - - -export function has_backing_array_buffer(js_obj: TypedArray): boolean { - return typeof SharedArrayBuffer !== "undefined" - ? js_obj.buffer instanceof ArrayBuffer || js_obj.buffer instanceof SharedArrayBuffer - : js_obj.buffer instanceof ArrayBuffer; -} \ No newline at end of file diff --git a/src/mono/browser/runtime/net6-legacy/corebindings.ts b/src/mono/browser/runtime/net6-legacy/corebindings.ts deleted file mode 100644 index fd4d85aa021304..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/corebindings.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { JSHandle, GCHandle, MonoObjectRef, MonoMethod, MonoObject, WasmRoot, PromiseController } from "../types/internal"; -import { mono_bind_method, _create_primitive_converters } from "./method-binding"; -import { mono_wasm_new_root } from "../roots"; -import { Module, runtimeHelpers } from "../globals"; -import cwraps from "../cwraps"; -import { legacyHelpers, wasm_type_symbol } from "./globals"; -import { find_corlib_class } from "../class-loader"; -type SigLine = [lazy: boolean, jsname: string, csname: string, signature: string/*ArgsMarshalString*/]; -const fn_signatures: SigLine[] = [ - [true, "_get_cs_owned_object_by_js_handle_ref", "GetCSOwnedObjectByJSHandleRef", "iim"], - [true, "_get_cs_owned_object_js_handle_ref", "GetCSOwnedObjectJSHandleRef", "mi"], - [true, "_try_get_cs_owned_object_js_handle_ref", "TryGetCSOwnedObjectJSHandleRef", "mi"], - [true, "_create_cs_owned_proxy_ref", "CreateCSOwnedProxyRef", "iiim"], - - [true, "_get_js_owned_object_by_gc_handle_ref", "GetJSOwnedObjectByGCHandleRef", "im"], - [true, "_get_js_owned_object_gc_handle_ref", "GetJSOwnedObjectGCHandleRef", "m"], - - [true, "_create_tcs", "CreateTaskSource", ""], - [true, "_set_tcs_result_ref", "SetTaskSourceResultRef", "iR"], - [true, "_set_tcs_failure", "SetTaskSourceFailure", "is"], - [true, "_get_tcs_task_ref", "GetTaskSourceTaskRef", "im"], - [true, "_setup_js_cont_ref", "SetupJSContinuationRef", "mo"], - - [true, "_object_to_string_ref", "ObjectToStringRef", "m"], - [true, "_get_date_value_ref", "GetDateValueRef", "m"], - [true, "_create_date_time_ref", "CreateDateTimeRef", "dm"], - [true, "_create_uri_ref", "CreateUriRef", "sm"], - [true, "_is_simple_array_ref", "IsSimpleArrayRef", "m"], - [true, "_get_call_sig_ref", "GetCallSignatureRef", "im"], -]; - -export interface LegacyExports { - // see src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\Interop\LegacyExports.cs - _get_cs_owned_object_by_js_handle_ref(jsHandle: JSHandle, shouldAddInflight: 0 | 1, result: MonoObjectRef): void; - _get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle; - _try_get_cs_owned_object_js_handle_ref(obj: MonoObjectRef, shouldAddInflight: 0 | 1): JSHandle; - _create_cs_owned_proxy_ref(jsHandle: JSHandle, mappedType: number, shouldAddInflight: 0 | 1, result: MonoObjectRef): void; - - _get_js_owned_object_by_gc_handle_ref(gcHandle: GCHandle, result: MonoObjectRef): void; - _get_js_owned_object_gc_handle_ref(obj: MonoObjectRef): GCHandle - - _create_tcs(): GCHandle; - _set_tcs_result_ref(gcHandle: GCHandle, result: any): void - _set_tcs_failure(gcHandle: GCHandle, result: string): void - _get_tcs_task_ref(gcHandle: GCHandle, result: MonoObjectRef): void; - _setup_js_cont_ref(task: MonoObjectRef, continuation: PromiseController): void; - - _object_to_string_ref(obj: MonoObjectRef): string; - _get_date_value_ref(obj: MonoObjectRef): number; - _create_date_time_ref(ticks: number, result: MonoObjectRef): void; - _create_uri_ref(uri: string, result: MonoObjectRef): void; - _is_simple_array_ref(obj: MonoObjectRef): boolean; - _get_call_sig_ref(method: MonoMethod, obj: WasmRoot): string; -} - -export const legacyManagedExports: LegacyExports = {}; - - -export function bind_runtime_method(method_name: string, signature: string): Function { - const method = get_method(method_name); - return mono_bind_method(method, signature, false, "BINDINGS_" + method_name); -} - -export function init_legacy_exports(): void { - // please keep System.Runtime.InteropServices.JavaScript.JSHostImplementation.MappedType in sync - (Object.prototype)[wasm_type_symbol] = 0; - (Array.prototype)[wasm_type_symbol] = 1; - (ArrayBuffer.prototype)[wasm_type_symbol] = 2; - (DataView.prototype)[wasm_type_symbol] = 3; - (Function.prototype)[wasm_type_symbol] = 4; - (Uint8Array.prototype)[wasm_type_symbol] = 11; - - const box_buffer_size = 65536; - legacyHelpers._unbox_buffer_size = 65536; - legacyHelpers._box_buffer = Module._malloc(box_buffer_size); - legacyHelpers._unbox_buffer = Module._malloc(legacyHelpers._unbox_buffer_size); - legacyHelpers._class_int32 = find_corlib_class("System", "Int32"); - legacyHelpers._class_uint32 = find_corlib_class("System", "UInt32"); - legacyHelpers._class_double = find_corlib_class("System", "Double"); - legacyHelpers._class_boolean = find_corlib_class("System", "Boolean"); - legacyHelpers._null_root = mono_wasm_new_root(); - _create_primitive_converters(); - - legacyHelpers.runtime_legacy_exports_classname = "LegacyExports"; - legacyHelpers.runtime_legacy_exports_class = cwraps.mono_wasm_assembly_find_class(runtimeHelpers.runtime_interop_module, runtimeHelpers.runtime_interop_namespace, legacyHelpers.runtime_legacy_exports_classname); - if (!legacyHelpers.runtime_legacy_exports_class) - throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + legacyHelpers.runtime_legacy_exports_classname + " class"; - - for (const sig of fn_signatures) { - const wf: any = legacyManagedExports; - const [lazy, jsname, csname, signature] = sig; - if (lazy) { - // lazy init on first run - wf[jsname] = function (...args: any[]) { - const fce = bind_runtime_method(csname, signature); - wf[jsname] = fce; - return fce(...args); - }; - } - else { - const fce = bind_runtime_method(csname, signature); - wf[jsname] = fce; - } - } -} - -export function get_method(method_name: string): MonoMethod { - const res = cwraps.mono_wasm_assembly_find_method(legacyHelpers.runtime_legacy_exports_class, method_name, -1); - if (!res) - throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + legacyHelpers.runtime_legacy_exports_classname + "." + method_name; - return res; -} \ No newline at end of file diff --git a/src/mono/browser/runtime/net6-legacy/cs-to-js.ts b/src/mono/browser/runtime/net6-legacy/cs-to-js.ts deleted file mode 100644 index f93a3d13c53fd5..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/cs-to-js.ts +++ /dev/null @@ -1,354 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { Int32Ptr, VoidPtr } from "../types/emscripten"; -import { MarshalType, MonoType, MarshalError, MonoTypeNull, MonoArray, MonoArrayNull, MonoObject, MonoObjectNull, GCHandle, MonoStringRef, MonoObjectRef, MonoString, JSHandleDisposed, is_nullish, WasmRoot } from "../types/internal"; -import { _are_promises_supported } from "../cancelable-promise"; -import { legacy_c_functions as cwraps } from "../cwraps"; -import { mono_wasm_get_jsobj_from_js_handle, _lookup_js_owned_object, setup_managed_proxy, mono_wasm_get_js_handle, teardown_managed_proxy, assert_not_disposed } from "../gc-handles"; -import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; -import { ManagedObject } from "../marshal"; -import { getU32, getI32, getF32, getF64, setI32_unchecked } from "../memory"; -import { mono_wasm_new_root, mono_wasm_new_external_root } from "../roots"; -import { monoStringToString, monoStringToStringUnsafe } from "../strings"; -import { legacyManagedExports } from "./corebindings"; -import { legacyHelpers } from "./globals"; -import { js_to_mono_obj_root } from "./js-to-cs"; -import { assert_legacy_interop, mono_bind_method, mono_method_get_call_signature_ref } from "./method-binding"; -import { createPromiseController } from "../globals"; - -const delegate_invoke_symbol = Symbol.for("wasm delegate_invoke"); - -// this is only used from Blazor -export function unbox_mono_obj(mono_obj: MonoObject): any { - assert_legacy_interop(); - - if (mono_obj === MonoObjectNull) - return undefined; - - const root = mono_wasm_new_root(mono_obj); - try { - return unbox_mono_obj_root(root); - } finally { - root.release(); - } -} - -function _unbox_cs_owned_root_as_js_object(root: WasmRoot) { - // we don't need in-flight reference as we already have it rooted here - const js_handle = legacyManagedExports._get_cs_owned_object_js_handle_ref(root.address, 0); - const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - return js_obj; -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root: WasmRoot, type: MarshalType, typePtr: MonoType, unbox_buffer: VoidPtr): any { - //See MARSHAL_TYPE_ defines in driver.c - switch (type) { - case MarshalType.NULL: - return null; - case MarshalType.INT64: - case MarshalType.UINT64: - // TODO: Fix this once emscripten offers HEAPI64/HEAPU64 or can return them - throw new Error("int64 not available"); - case MarshalType.STRING: - case MarshalType.STRING_INTERNED: - return monoStringToString(root); - case MarshalType.VT: - throw new Error("no idea on how to unbox value types"); - case MarshalType.DELEGATE: - return _wrap_delegate_root_as_function(root); - case MarshalType.TASK: - return _unbox_task_root_as_promise(root); - case MarshalType.OBJECT: - return _unbox_ref_type_root_as_js_object(root); - case MarshalType.ARRAY_BYTE: - case MarshalType.ARRAY_UBYTE: - case MarshalType.ARRAY_UBYTE_C: - case MarshalType.ARRAY_SHORT: - case MarshalType.ARRAY_USHORT: - case MarshalType.ARRAY_INT: - case MarshalType.ARRAY_UINT: - case MarshalType.ARRAY_FLOAT: - case MarshalType.ARRAY_DOUBLE: - throw new Error("Marshaling of primitive arrays are not supported."); - case 20: // clr .NET DateTime - return new Date(legacyManagedExports._get_date_value_ref(root.address)); - case 21: // clr .NET DateTimeOffset - return legacyManagedExports._object_to_string_ref(root.address); - case MarshalType.URI: - return legacyManagedExports._object_to_string_ref(root.address); - case MarshalType.SAFEHANDLE: - return _unbox_cs_owned_root_as_js_object(root); - case MarshalType.VOID: - return undefined; - default: - throw new Error(`no idea on how to unbox object of MarshalType ${type} at offset ${root.value} (root address is ${root.address})`); - } -} - -export function _unbox_mono_obj_root_with_known_nonprimitive_type(root: WasmRoot, type: MarshalType, unbox_buffer: VoidPtr): any { - if (type >= MarshalError.FIRST) - throw new Error(`Got marshaling error ${type} when attempting to unbox object at address ${root.value} (root located at ${root.address})`); - - let typePtr = MonoTypeNull; - if ((type === MarshalType.VT) || (type == MarshalType.OBJECT)) { - typePtr = getU32(unbox_buffer); - if (typePtr < 1024) - throw new Error(`Got invalid MonoType ${typePtr} for object at address ${root.value} (root located at ${root.address})`); - } - - return _unbox_mono_obj_root_with_known_nonprimitive_type_impl(root, type, typePtr, unbox_buffer); -} - -export function unbox_mono_obj_root(root: WasmRoot): any { - if (root.value === 0) - return undefined; - - const unbox_buffer = legacyHelpers._unbox_buffer; - const type = cwraps.mono_wasm_try_unbox_primitive_and_get_type_ref(root.address, unbox_buffer, legacyHelpers._unbox_buffer_size); - switch (type) { - case MarshalType.INT: - return getI32(unbox_buffer); - case MarshalType.UINT32: - return getU32(unbox_buffer); - case MarshalType.POINTER: - // FIXME: Is this right? - return getU32(unbox_buffer); - case MarshalType.FP32: - return getF32(unbox_buffer); - case MarshalType.FP64: - return getF64(unbox_buffer); - case MarshalType.BOOL: - return (getI32(unbox_buffer)) !== 0; - case MarshalType.CHAR: - return String.fromCharCode(getI32(unbox_buffer)); - case MarshalType.NULL: - return null; - default: - return _unbox_mono_obj_root_with_known_nonprimitive_type(root, type, unbox_buffer); - } -} - -export function mono_array_to_js_array(mono_array: MonoArray): any[] | null { - assert_legacy_interop(); - if (mono_array === MonoArrayNull) - return null; - - const arrayRoot = mono_wasm_new_root(mono_array); - try { - return mono_array_root_to_js_array(arrayRoot); - } finally { - arrayRoot.release(); - } -} - -function is_nested_array_ref(ele: WasmRoot) { - return legacyManagedExports._is_simple_array_ref(ele.address); -} - -export function mono_array_root_to_js_array(arrayRoot: WasmRoot): any[] | null { - if (arrayRoot.value === MonoArrayNull) - return null; - - const arrayAddress = arrayRoot.address; - const elemRoot = mono_wasm_new_root(); - const elemAddress = elemRoot.address; - - try { - const len = cwraps.mono_wasm_array_length_ref(arrayAddress); - const res = new Array(len); - for (let i = 0; i < len; ++i) { - // TODO: pass arrayRoot.address and elemRoot.address into new API that copies - cwraps.mono_wasm_array_get_ref(arrayAddress, i, elemAddress); - - if (is_nested_array_ref(elemRoot)) - res[i] = mono_array_root_to_js_array(elemRoot); - else - res[i] = unbox_mono_obj_root(elemRoot); - } - return res; - } finally { - elemRoot.release(); - } -} - -export function _wrap_delegate_root_as_function(root: WasmRoot): Function | null { - if (root.value === MonoObjectNull) - return null; - - // get strong reference to the Delegate - const gc_handle = legacyManagedExports._get_js_owned_object_gc_handle_ref(root.address); - return _wrap_delegate_gc_handle_as_function(gc_handle); -} - -export function _wrap_delegate_gc_handle_as_function(gc_handle: GCHandle): Function { - // see if we have js owned instance for this gc_handle already - let result = _lookup_js_owned_object(gc_handle); - - - // If the function for this gc_handle was already collected (or was never created) - if (!result) { - - // note that we do not implement function/delegate roundtrip - result = function (...args: any[]) { - assert_not_disposed(result); - const boundMethod = result[delegate_invoke_symbol]; - return boundMethod(...args); - }; - - // bind the method - const delegateRoot = mono_wasm_new_root(); - get_js_owned_object_by_gc_handle_ref(gc_handle, delegateRoot.address); - try { - if (typeof result[delegate_invoke_symbol] === "undefined") { - const method = cwraps.mono_wasm_get_delegate_invoke_ref(delegateRoot.address); - const signature = mono_method_get_call_signature_ref(method, delegateRoot); - const js_method = mono_bind_method(method, signature, true); - result[delegate_invoke_symbol] = js_method.bind({ this_arg_gc_handle: gc_handle }); - if (!result[delegate_invoke_symbol]) { - throw new Error("System.Delegate Invoke method can not be resolved."); - } - } - } finally { - delegateRoot.release(); - } - - setup_managed_proxy(result, gc_handle); - } else { - assert_not_disposed(result); - } - - return result; -} - -export function mono_wasm_create_cs_owned_object_ref(core_name: MonoStringRef, args: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const argsRoot = mono_wasm_new_external_root(args), - nameRoot = mono_wasm_new_external_root(core_name), - resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_name = monoStringToString(nameRoot); - if (!js_name) { - wrap_error_root(is_exception, "Invalid name @" + nameRoot.value, resultRoot); - return; - } - - const coreObj = (globalThis)[js_name]; - if (coreObj === null || typeof coreObj === "undefined") { - wrap_error_root(is_exception, "JavaScript host object '" + js_name + "' not found.", resultRoot); - return; - } - - try { - const js_args = mono_array_root_to_js_array(argsRoot); - - // This is all experimental !!!!!! - const allocator = function (constructor: Function, js_args: any[] | null) { - // Not sure if we should be checking for anything here - let argsList = []; - argsList[0] = constructor; - if (js_args) - argsList = argsList.concat(js_args); - // eslint-disable-next-line prefer-spread - const tempCtor = constructor.bind.apply(constructor, argsList); - const js_obj = new tempCtor(); - return js_obj; - }; - - const js_obj = allocator(coreObj, js_args); - const js_handle = mono_wasm_get_js_handle(js_obj); - // returns boxed js_handle int, because on exception we need to return String on same method signature - // here we don't have anything to in-flight reference, as the JSObject doesn't exist yet - js_to_mono_obj_root(js_handle, resultRoot, false); - wrap_no_error_root(is_exception); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - return; - } - } finally { - resultRoot.release(); - argsRoot.release(); - nameRoot.release(); - } -} - -function _unbox_task_root_as_promise(root: WasmRoot) { - if (root.value === MonoObjectNull) - return null; - - if (!_are_promises_supported) - throw new Error("Promises are not supported thus 'System.Threading.Tasks.Task' can not work in this context."); - - // get strong reference to Task - const gc_handle = legacyManagedExports._get_js_owned_object_gc_handle_ref(root.address); - - // see if we have js owned instance for this gc_handle already - let result = _lookup_js_owned_object(gc_handle); - - // If the promise for this gc_handle was already collected (or was never created) - if (!result) { - const explicitFinalization = () => teardown_managed_proxy(result, gc_handle); - - const { promise, promise_control } = createPromiseController(explicitFinalization, explicitFinalization); - - // note that we do not implement promise/task roundtrip - // With more complexity we could recover original instance when this promise is marshaled back to C#. - result = promise; - - // register C# side of the continuation - legacyManagedExports._setup_js_cont_ref(root.address, promise_control); - - setup_managed_proxy(result, gc_handle); - } - - return result; -} - -export function _unbox_ref_type_root_as_js_object(root: WasmRoot): any { - - if (root.value === MonoObjectNull) - return null; - - // this could be JSObject proxy of a js native object - // we don't need in-flight reference as we already have it rooted here - const js_handle = legacyManagedExports._try_get_cs_owned_object_js_handle_ref(root.address, 0); - if (js_handle) { - if (js_handle === JSHandleDisposed) { - throw new Error("Cannot access a disposed JSObject at " + root.value); - } - return mono_wasm_get_jsobj_from_js_handle(js_handle); - } - // otherwise this is C# only object - - // get strong reference to Object - const gc_handle = legacyManagedExports._get_js_owned_object_gc_handle_ref(root.address); - - // see if we have js owned instance for this gc_handle already - let result = _lookup_js_owned_object(gc_handle); - - // If the JS object for this gc_handle was already collected (or was never created) - if (is_nullish(result)) { - result = new ManagedObject(); - - setup_managed_proxy(result, gc_handle); - } - - return result; -} - -export function get_js_owned_object_by_gc_handle_ref(gc_handle: GCHandle, result: MonoObjectRef): void { - if (!gc_handle) { - setI32_unchecked(result, 0); - return; - } - // this is always strong gc_handle - legacyManagedExports._get_js_owned_object_by_gc_handle_ref(gc_handle, result); -} - -/** - * @deprecated Not GC or thread safe - */ -export function conv_string(mono_obj: MonoString): string | null { - assert_legacy_interop(); - return monoStringToStringUnsafe(mono_obj); -} \ No newline at end of file diff --git a/src/mono/browser/runtime/net6-legacy/export-types.ts b/src/mono/browser/runtime/net6-legacy/export-types.ts deleted file mode 100644 index 7322fa543cb00b..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/export-types.ts +++ /dev/null @@ -1,243 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import type { MemOffset, MonoArray, MonoObject, MonoObjectRef, MonoString, NumberOrPointer, WasmRoot, WasmRootBuffer } from "../types/internal"; -import type { VoidPtr } from "../types/emscripten"; - -/** - * @deprecated Please use methods in top level API object instead - */ -export type BINDINGType = { - /** - * @deprecated Please use [JSExportAttribute] instead - */ - bind_static_method: (fqn: string, signature?: string) => Function; - /** - * @deprecated Please use runMain() instead - */ - call_assembly_entry_point: (assembly: string, args?: any[], signature?: string) => number; - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_new: (size: number) => MonoArray; - /** - * @deprecated Not GC or thread safe - */ - mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void; - /** - * @deprecated Not GC or thread safe - */ - js_string_to_mono_string: (string: string) => MonoString; - /** - * @deprecated Not GC or thread safe - */ - js_typed_array_to_array: (js_obj: any) => MonoArray; - /** - * @deprecated Not GC or thread safe - */ - mono_array_to_js_array: (mono_array: MonoArray) => any[] | null; - /** - * @deprecated Not GC or thread safe - */ - js_to_mono_obj: (js_obj: any) => MonoObject; - /** - * @deprecated Not GC or thread safe - */ - conv_string: (mono_obj: MonoString) => string | null; - /** - * @deprecated Not GC or thread safe - */ - unbox_mono_obj: (mono_obj: MonoObject) => any; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_obj_array_new_ref: (size: number, result: MonoObjectRef) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_obj_array_set_ref: (array: MonoObjectRef, idx: number, obj: MonoObjectRef) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - js_string_to_mono_string_root: (string: string, result: WasmRoot) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - js_typed_array_to_array_root: (js_obj: any, result: WasmRoot) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - js_to_mono_obj_root: (js_obj: any, result: WasmRoot, should_add_in_flight: boolean) => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - conv_string_root: (root: WasmRoot) => string | null; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - unbox_mono_obj_root: (root: WasmRoot) => any; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_array_root_to_js_array: (arrayRoot: WasmRoot) => any[] | null; -}; - -/** - * @deprecated Please use methods in top level API object instead - */ -export type MONOType = { - /** - * @deprecated Please use setEnvironmentVariable() instead - */ - mono_wasm_setenv: (name: string, value: string) => void; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_load_bytes_into_heap: (bytes: Uint8Array) => VoidPtr; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_load_icu_data: (offset: VoidPtr) => boolean; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_runtime_ready: () => void; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_new_root_buffer: (capacity: number, name?: string) => WasmRootBuffer; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_new_root: (value?: T | undefined) => WasmRoot; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_new_external_root: (address: VoidPtr | MonoObjectRef) => WasmRoot; - /** - * @deprecated Please use [JSImportAttribute] or [JSExportAttribute] for interop instead. - */ - mono_wasm_release_roots: (...args: WasmRoot[]) => void; - /** - * @deprecated Please use runMain instead - */ - mono_run_main: (main_assembly_name: string, args: string[]) => Promise; - /** - * @deprecated Please use runMainAndExit instead - */ - mono_run_main_and_exit: (main_assembly_name: string, args: string[]) => Promise; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number; - /** - * @deprecated Please use config.assets instead - */ - mono_wasm_load_runtime: (unused: string, debugLevel: number) => void; - /** - * @deprecated Please use getConfig() instead - */ - config: any; - /** - * @deprecated Please use config.assets instead - */ - loaded_files: string[]; - /** - * @deprecated Please use setHeapB32 - */ - setB32: (offset: MemOffset, value: number | boolean) => void; - /** - * @deprecated Please use setHeapI8 - */ - setI8: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI16 - */ - setI16: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI32 - */ - setI32: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI52 - */ - setI52: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapU52 - */ - setU52: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapI64Big - */ - setI64Big: (offset: MemOffset, value: bigint) => void; - /** - * @deprecated Please use setHeapU8 - */ - setU8: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapU16 - */ - setU16: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapU32 - */ - setU32: (offset: MemOffset, value: NumberOrPointer) => void; - /** - * @deprecated Please use setHeapF32 - */ - setF32: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use setHeapF64 - */ - setF64: (offset: MemOffset, value: number) => void; - /** - * @deprecated Please use getHeapB32 - */ - getB32: (offset: MemOffset) => boolean; - /** - * @deprecated Please use getHeapI8 - */ - getI8: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI16 - */ - getI16: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI32 - */ - getI32: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI52 - */ - getI52: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapU52 - */ - getU52: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapI64Big - */ - getI64Big: (offset: MemOffset) => bigint; - /** - * @deprecated Please use getHeapU8 - */ - getU8: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapU16 - */ - getU16: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapU32 - */ - getU32: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapF32 - */ - getF32: (offset: MemOffset) => number; - /** - * @deprecated Please use getHeapF64 - */ - getF64: (offset: MemOffset) => number; -}; - -export { MonoArray, MonoObject, MonoString }; diff --git a/src/mono/browser/runtime/net6-legacy/exports-legacy.ts b/src/mono/browser/runtime/net6-legacy/exports-legacy.ts deleted file mode 100644 index b2f8aeda4953b6..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/exports-legacy.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { legacy_c_functions as cwraps } from "../cwraps"; -import { mono_wasm_runtime_ready } from "../debug"; -import { mono_wasm_load_icu_data } from "../icu"; -import { mono_wasm_load_bytes_into_heap, setB32, setI8, setI16, setI32, setI52, setU52, setI64Big, setU8, setU16, setU32, setF32, setF64, getB32, getI8, getI16, getI32, getI52, getU52, getI64Big, getU8, getU16, getU32, getF32, getF64 } from "../memory"; -import { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_new_external_root, mono_wasm_release_roots } from "../roots"; -import { mono_run_main, mono_run_main_and_exit } from "../run"; -import { mono_wasm_setenv } from "../startup"; -import { stringToMonoStringRoot, monoStringToString } from "../strings"; -import { mono_array_to_js_array, unbox_mono_obj, unbox_mono_obj_root, mono_array_root_to_js_array, conv_string } from "./cs-to-js"; -import { js_typed_array_to_array, js_to_mono_obj, js_typed_array_to_array_root, js_to_mono_obj_root } from "./js-to-cs"; -import { mono_bind_static_method, mono_call_assembly_entry_point } from "./method-calls"; -import { mono_wasm_load_runtime } from "../startup"; -import { BINDINGType, MONOType } from "./export-types"; -import { mono_method_resolve } from "./method-binding"; -import { runtimeHelpers } from "../globals"; -import { stringToMonoStringIntern, stringToMonoStringUnsafe } from "./strings"; - -export function export_mono_api(): MONOType { - return { - // legacy MONO API - mono_wasm_setenv, - mono_wasm_load_bytes_into_heap, - mono_wasm_load_icu_data, - mono_wasm_runtime_ready, - mono_wasm_new_root_buffer, - mono_wasm_new_root, - mono_wasm_new_external_root, - mono_wasm_release_roots, - mono_run_main, - mono_run_main_and_exit, - - // for Blazor's future! - mono_wasm_add_assembly: null, - mono_wasm_load_runtime, - - config: runtimeHelpers.config, - loaded_files: [], - - // memory accessors - setB32, - setI8, - setI16, - setI32, - setI52, - setU52, - setI64Big, - setU8, - setU16, - setU32, - setF32, - setF64, - getB32, - getI8, - getI16, - getI32, - getI52, - getU52, - getI64Big, - getU8, - getU16, - getU32, - getF32, - getF64, - }; -} - -export function cwraps_mono_api(mono: MONOType): void { - Object.assign(mono, { - mono_wasm_add_assembly: cwraps.mono_wasm_add_assembly, - }); -} - -export function export_internal_api(): any { - return { - stringToMonoStringIntern, // MarshalTests.cs - mono_method_resolve, //MarshalTests.cs - }; -} - -export function export_binding_api(): BINDINGType { - return { - // legacy BINDING API - bind_static_method: mono_bind_static_method, - call_assembly_entry_point: mono_call_assembly_entry_point, - mono_obj_array_new: null, - mono_obj_array_set: null, - js_string_to_mono_string: stringToMonoStringUnsafe, - js_typed_array_to_array, - mono_array_to_js_array, - js_to_mono_obj, - conv_string, - unbox_mono_obj, - - mono_obj_array_new_ref: null, - mono_obj_array_set_ref: null, - js_string_to_mono_string_root: stringToMonoStringRoot, - js_typed_array_to_array_root, - js_to_mono_obj_root, - conv_string_root: monoStringToString, - unbox_mono_obj_root, - mono_array_root_to_js_array, - }; -} - -export function cwraps_binding_api(binding: BINDINGType): void { - Object.assign(binding, { - mono_obj_array_new: cwraps.mono_wasm_obj_array_new, - mono_obj_array_set: cwraps.mono_wasm_obj_array_set, - mono_obj_array_new_ref: cwraps.mono_wasm_obj_array_new_ref, - mono_obj_array_set_ref: cwraps.mono_wasm_obj_array_set_ref, - }); -} diff --git a/src/mono/browser/runtime/net6-legacy/globals.ts b/src/mono/browser/runtime/net6-legacy/globals.ts deleted file mode 100644 index 3aabbe0830f380..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/globals.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import type { GlobalObjects, MonoClass } from "../types/internal"; -import type { VoidPtr } from "../types/emscripten"; -import type { BINDINGType, MONOType } from "./export-types"; - -export let MONO: MONOType; -export let BINDING: BINDINGType; - -export const legacyHelpers: LegacyHelpers = { -}; - -export function initializeLegacyExports( - globals: GlobalObjects, -): void { - MONO = globals.mono; - BINDING = globals.binding; -} - -export type LegacyHelpers = { - runtime_legacy_exports_classname: string; - runtime_legacy_exports_class: MonoClass; - - // A WasmRoot that is guaranteed to contain 0 - _null_root: any; - _class_int32: MonoClass; - _class_uint32: MonoClass; - _class_double: MonoClass; - _class_boolean: MonoClass; - _unbox_buffer_size: number; - _box_buffer: VoidPtr; - _unbox_buffer: VoidPtr; - _box_root: any; -} - -export const wasm_type_symbol = Symbol.for("wasm type"); diff --git a/src/mono/browser/runtime/net6-legacy/js-to-cs.ts b/src/mono/browser/runtime/net6-legacy/js-to-cs.ts deleted file mode 100644 index 9e9f5d9bb7c028..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/js-to-cs.ts +++ /dev/null @@ -1,294 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { isThenable } from "../cancelable-promise"; -import { legacy_c_functions as cwraps } from "../cwraps"; -import { js_owned_gc_handle_symbol, assert_not_disposed, cs_owned_js_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, mono_wasm_release_cs_owned_object, teardown_managed_proxy, mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; -import { Module } from "../globals"; -import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; -import { setI32_unchecked, setU32_unchecked, setF64, setB32, localHeapViewU8 } from "../memory"; -import { mono_wasm_new_root, mono_wasm_release_roots, mono_wasm_new_external_root } from "../roots"; -import { stringToMonoStringRoot, stringToInternedMonoStringRoot } from "../strings"; -import { MonoObject, is_nullish, MonoClass, MonoArray, MonoObjectNull, JSHandle, MonoObjectRef, JSHandleNull, JSHandleDisposed, WasmRoot } from "../types/internal"; -import { TypedArray, Int32Ptr } from "../types/emscripten"; -import { has_backing_array_buffer } from "./buffers"; -import { legacyManagedExports } from "./corebindings"; -import { get_js_owned_object_by_gc_handle_ref } from "./cs-to-js"; -import { legacyHelpers, wasm_type_symbol } from "./globals"; -import { assert_legacy_interop } from "./method-binding"; - -export function _js_to_mono_uri_root(should_add_in_flight: boolean, js_obj: any, result: WasmRoot): void { - switch (true) { - case js_obj === null: - case typeof js_obj === "undefined": - result.clear(); - return; - case typeof js_obj === "symbol": - case typeof js_obj === "string": - legacyManagedExports._create_uri_ref(js_obj, result.address); - return; - default: - _extract_mono_obj_root(should_add_in_flight, js_obj, result); - return; - } -} - -// this is only used from Blazor -/** - * @deprecated Not GC or thread safe. For blazor use only - */ -export function js_to_mono_obj(js_obj: any): MonoObject { - assert_legacy_interop(); - const temp = mono_wasm_new_root(); - try { - js_to_mono_obj_root(js_obj, temp, false); - return temp.value; - } finally { - temp.release(); - } -} - -/** - * @deprecated Not GC or thread safe - */ -export function _js_to_mono_obj_unsafe(should_add_in_flight: boolean, js_obj: any): MonoObject { - const temp = mono_wasm_new_root(); - try { - js_to_mono_obj_root(js_obj, temp, should_add_in_flight); - return temp.value; - } finally { - temp.release(); - } -} - -export function js_to_mono_obj_root(js_obj: any, result: WasmRoot, should_add_in_flight: boolean): void { - assert_legacy_interop(); - - if (is_nullish(result)) - throw new Error("Expected (value, WasmRoot, boolean)"); - - switch (true) { - case js_obj === null: - case typeof js_obj === "undefined": - result.clear(); - return; - case typeof js_obj === "number": { - let box_class: MonoClass; - if ((js_obj | 0) === js_obj) { - setI32_unchecked(legacyHelpers._box_buffer, js_obj); - box_class = legacyHelpers._class_int32; - } else if ((js_obj >>> 0) === js_obj) { - setU32_unchecked(legacyHelpers._box_buffer, js_obj); - box_class = legacyHelpers._class_uint32; - } else { - setF64(legacyHelpers._box_buffer, js_obj); - box_class = legacyHelpers._class_double; - } - - cwraps.mono_wasm_box_primitive_ref(box_class, legacyHelpers._box_buffer, 8, result.address); - return; - } - case typeof js_obj === "string": - stringToMonoStringRoot(js_obj, result); - return; - case typeof js_obj === "symbol": - stringToInternedMonoStringRoot(js_obj, result); - return; - case typeof js_obj === "boolean": - setB32(legacyHelpers._box_buffer, js_obj); - cwraps.mono_wasm_box_primitive_ref(legacyHelpers._class_boolean, legacyHelpers._box_buffer, 4, result.address); - return; - case isThenable(js_obj) === true: { - _wrap_js_thenable_as_task_root(js_obj, result); - return; - } - case js_obj.constructor.name === "Date": - // getTime() is always UTC - legacyManagedExports._create_date_time_ref(js_obj.getTime(), result.address); - return; - default: - _extract_mono_obj_root(should_add_in_flight, js_obj, result); - return; - } -} - -function _extract_mono_obj_root(should_add_in_flight: boolean, js_obj: any, result: WasmRoot): void { - result.clear(); - - if (js_obj === null || typeof js_obj === "undefined") - return; - - if (js_obj[js_owned_gc_handle_symbol] !== undefined) { - // for js_owned_gc_handle we don't want to create new proxy - // since this is strong gc_handle we don't need to in-flight reference - const gc_handle = assert_not_disposed(js_obj); - get_js_owned_object_by_gc_handle_ref(gc_handle, result.address); - return; - } - if (js_obj[cs_owned_js_handle_symbol]) { - get_cs_owned_object_by_js_handle_ref(js_obj[cs_owned_js_handle_symbol], should_add_in_flight, result.address); - - // It's possible the managed object corresponding to this JS object was collected, - // in which case we need to make a new one. - // FIXME: This check is not thread safe - if (!result.value) { - delete js_obj[cs_owned_js_handle_symbol]; - } - } - - // FIXME: This check is not thread safe - if (!result.value) { - // Obtain the JS -> C# type mapping. - const wasm_type = js_obj[wasm_type_symbol]; - const wasm_type_id = typeof wasm_type === "undefined" ? 0 : wasm_type; - - const js_handle = mono_wasm_get_js_handle(js_obj); - - legacyManagedExports._create_cs_owned_proxy_ref(js_handle, wasm_type_id, should_add_in_flight ? 1 : 0, result.address); - } -} - -// https://github.com/Planeshifter/emscripten-examples/blob/master/01_PassingArrays/sum_post.js -function js_typedarray_to_heap(typedArray: TypedArray) { - assert_legacy_interop(); - const numBytes = typedArray.length * typedArray.BYTES_PER_ELEMENT; - const ptr = Module._malloc(numBytes); - const heapU8 = localHeapViewU8(); - const heapBytes = new Uint8Array(heapU8.buffer, ptr, numBytes); - heapBytes.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, numBytes)); - // WARNING: returned memory view will get stale when linear memory grows on another thread. This is legacy interop so we don't try to fix it. The view will be fine when used in synchronous calls. - return heapBytes; -} - -export function js_typed_array_to_array_root(js_obj: any, result: WasmRoot): void { - // JavaScript typed arrays are array-like objects and provide a mechanism for accessing - // raw binary data. (...) To achieve maximum flexibility and efficiency, JavaScript typed arrays - // split the implementation into buffers and views. A buffer (implemented by the ArrayBuffer object) - // is an object representing a chunk of data; it has no format to speak of, and offers no - // mechanism for accessing its contents. In order to access the memory contained in a buffer, - // you need to use a view. A view provides a context - that is, a data type, starting offset, - // and number of elements - that turns the data into an actual typed array. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays - if (has_backing_array_buffer(js_obj) && js_obj.BYTES_PER_ELEMENT) { - const arrayType = js_obj[wasm_type_symbol]; - const heapBytes = js_typedarray_to_heap(js_obj); - cwraps.mono_wasm_typed_array_new_ref(heapBytes.byteOffset, js_obj.length, js_obj.BYTES_PER_ELEMENT, arrayType, result.address); - Module._free(heapBytes.byteOffset); - } - else { - throw new Error("Object '" + js_obj + "' is not a typed array"); - } -} - -/** - * @deprecated Not GC or thread safe - */ -export function js_typed_array_to_array(js_obj: any): MonoArray { - const temp = mono_wasm_new_root(); - try { - js_typed_array_to_array_root(js_obj, temp); - return temp.value; - } finally { - temp.release(); - } -} - -export function js_to_mono_enum(js_obj: any): number { - if (typeof (js_obj) !== "number") - throw new Error(`Expected numeric value for enum argument, got '${js_obj}'`); - - return js_obj | 0; -} - -export function js_array_to_mono_array(js_array: any[], asString: boolean, should_add_in_flight: boolean): MonoArray { - const arrayRoot = mono_wasm_new_root(); - if (asString) - cwraps.mono_wasm_string_array_new_ref(js_array.length, arrayRoot.address); - else - cwraps.mono_wasm_obj_array_new_ref(js_array.length, arrayRoot.address); - const elemRoot = mono_wasm_new_root(MonoObjectNull); - const arrayAddress = arrayRoot.address; - const elemAddress = elemRoot.address; - - try { - for (let i = 0; i < js_array.length; ++i) { - let obj = js_array[i]; - if (asString) - obj = obj.toString(); - - js_to_mono_obj_root(obj, elemRoot, should_add_in_flight); - cwraps.mono_wasm_obj_array_set_ref(arrayAddress, i, elemAddress); - } - - return arrayRoot.value; - } finally { - mono_wasm_release_roots(arrayRoot, elemRoot); - } -} - -export function _wrap_js_thenable_as_task_root(thenable: Promise, resultRoot: WasmRoot): { - then_js_handle: JSHandle, -} { - if (!thenable) { - resultRoot.clear(); - return null; - } - - // hold strong JS reference to thenable while in flight - // ideally, this should be hold alive by lifespan of the resulting C# Task, but this is good cheap aproximation - const thenable_js_handle = mono_wasm_get_js_handle(thenable); - - // Note that we do not implement promise/task roundtrip. - // With more complexity we could recover original instance when this Task is marshaled back to JS. - // TODO optimization: return the tcs.Task on this same call instead of _get_tcs_task - const tcs_gc_handle = legacyManagedExports._create_tcs(); - const holder: any = { tcs_gc_handle }; - setup_managed_proxy(holder, tcs_gc_handle); - thenable.then((result) => { - legacyManagedExports._set_tcs_result_ref(tcs_gc_handle, result); - }, (reason) => { - legacyManagedExports._set_tcs_failure(tcs_gc_handle, reason ? reason.toString() : ""); - }).finally(() => { - // let go of the thenable reference - mono_wasm_release_cs_owned_object(thenable_js_handle); - teardown_managed_proxy(holder, tcs_gc_handle); // this holds holder alive for finalizer, until the promise is freed - }); - - - legacyManagedExports._get_tcs_task_ref(tcs_gc_handle, resultRoot.address); - - // returns raw pointer to tcs.Task - return { - then_js_handle: thenable_js_handle, - }; -} - -export function mono_wasm_typed_array_to_array_ref(js_handle: JSHandle, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - const resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(js_obj)) { - wrap_error_root(is_exception, "ERR06: Invalid JS object handle '" + js_handle + "'", resultRoot); - return; - } - - // returns pointer to C# array - js_typed_array_to_array_root(js_obj, resultRoot); - wrap_no_error_root(is_exception); - } catch (exc) { - wrap_error_root(is_exception, String(exc), resultRoot); - } finally { - resultRoot.release(); - } -} - -// when should_add_in_flight === true, the JSObject would be temporarily hold by Normal gc_handle, so that it would not get collected during transition to the managed stack. -// its InFlight gc_handle would be freed when the instance arrives to managed side via Interop.Runtime.ReleaseInFlight -export function get_cs_owned_object_by_js_handle_ref(js_handle: JSHandle, should_add_in_flight: boolean, result: MonoObjectRef): void { - if (js_handle === JSHandleNull || js_handle === JSHandleDisposed) { - setI32_unchecked(result, 0); - return; - } - legacyManagedExports._get_cs_owned_object_by_js_handle_ref(js_handle, should_add_in_flight ? 1 : 0, result); -} - diff --git a/src/mono/browser/runtime/net6-legacy/method-binding.ts b/src/mono/browser/runtime/net6-legacy/method-binding.ts deleted file mode 100644 index aa710700924e8b..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/method-binding.ts +++ /dev/null @@ -1,682 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import MonoWasmThreads from "consts:monoWasmThreads"; - -import { legacy_c_functions as cwraps } from "../cwraps"; -import { ENVIRONMENT_IS_PTHREAD, Module, mono_assert } from "../globals"; -import { parseFQN } from "../invoke-cs"; -import { setI32, setU32, setF32, setF64, setU52, setI52, setB32, setI32_unchecked, setU32_unchecked, _zero_region, _create_temp_frame, getB32, getI32, getU32, getF32, getF64 } from "../memory"; -import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; -import { stringToMonoStringRoot, stringToInternedMonoStringRoot, monoStringToString } from "../strings"; -import { MonoMethod, MonoObject, VoidPtrNull, MarshalType, MonoString, MonoObjectNull, WasmRootBuffer, WasmRoot } from "../types/internal"; -import { VoidPtr } from "../types/emscripten"; -import { legacyManagedExports } from "./corebindings"; -import { get_js_owned_object_by_gc_handle_ref, _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js"; -import { legacyHelpers } from "./globals"; -import { js_to_mono_obj_root, _js_to_mono_uri_root, js_to_mono_enum } from "./js-to-cs"; -import { _teardown_after_call } from "./method-calls"; -import { mono_log_warn } from "../logging"; -import { assert_js_interop } from "../invoke-js"; - - -const escapeRE = /[^A-Za-z0-9_$]/g; -const primitiveConverters = new Map(); -const _signature_converters = new Map(); -const boundMethodsByMethod: Map = new Map(); - -function _create_named_function(name: string, argumentNames: string[], body: string, closure: any): Function { - let result = null; - let closureArgumentList: any[] | null = null; - let closureArgumentNames = null; - - if (closure) { - closureArgumentNames = Object.keys(closure); - closureArgumentList = new Array(closureArgumentNames.length); - for (let i = 0, l = closureArgumentNames.length; i < l; i++) - closureArgumentList[i] = closure[closureArgumentNames[i]]; - } - - const constructor = _create_rebindable_named_function(name, argumentNames, body, closureArgumentNames); - // eslint-disable-next-line prefer-spread - result = constructor.apply(null, closureArgumentList); - - return result; -} - -function _create_rebindable_named_function(name: string, argumentNames: string[], body: string, closureArgNames: string[] | null): Function { - const strictPrefix = "\"use strict\";\r\n"; - let uriPrefix = "", escapedFunctionIdentifier = ""; - - if (name) { - uriPrefix = "//# sourceURL=https://dotnet.generated.invalid/" + name + "\r\n"; - escapedFunctionIdentifier = name; - } else { - escapedFunctionIdentifier = "unnamed"; - } - - let rawFunctionText = "function " + escapedFunctionIdentifier + "(" + - argumentNames.join(", ") + - ") {\r\n" + - body + - "\r\n};\r\n"; - - const lineBreakRE = /\r(\n?)/g; - - rawFunctionText = - uriPrefix + strictPrefix + - rawFunctionText.replace(lineBreakRE, "\r\n ") + - ` return ${escapedFunctionIdentifier};\r\n`; - - let result = null, keys = null; - - if (closureArgNames) { - keys = closureArgNames.concat([rawFunctionText]); - } else { - keys = [rawFunctionText]; - } - - result = Function.apply(Function, keys); - return result; -} - -export function _create_primitive_converters(): void { - const result = primitiveConverters; - result.set("m", { steps: [{}], size: 0 }); - result.set("s", { steps: [{ convert_root: stringToMonoStringRoot.bind(Module) }], size: 0, needs_root: true }); - result.set("S", { steps: [{ convert_root: stringToInternedMonoStringRoot.bind(Module) }], size: 0, needs_root: true }); - // note we also bind first argument to false for both _js_to_mono_obj and _js_to_mono_uri, - // because we will root the reference, so we don't need in-flight reference - // also as those are callback arguments and we don't have platform code which would release the in-flight reference on C# end - result.set("o", { steps: [{ convert_root: js_to_mono_obj_root.bind(Module) }], size: 0, needs_root: true }); - result.set("u", { steps: [{ convert_root: _js_to_mono_uri_root.bind(Module, false) }], size: 0, needs_root: true }); - // ref object aka T&& - result.set("R", { steps: [{ convert_root: js_to_mono_obj_root.bind(Module), byref: true }], size: 0, needs_root: true }); - - // result.set ('k', { steps: [{ convert: js_to_mono_enum.bind (this), indirect: 'i64'}], size: 8}); - result.set("j", { steps: [{ convert: js_to_mono_enum.bind(Module), indirect: "i32" }], size: 8 }); - - result.set("b", { steps: [{ indirect: "bool" }], size: 8 }); - result.set("i", { steps: [{ indirect: "i32" }], size: 8 }); - result.set("I", { steps: [{ indirect: "u32" }], size: 8 }); - result.set("l", { steps: [{ indirect: "i52" }], size: 8 }); - result.set("L", { steps: [{ indirect: "u52" }], size: 8 }); - result.set("f", { steps: [{ indirect: "float" }], size: 8 }); - result.set("d", { steps: [{ indirect: "double" }], size: 8 }); -} - -function _create_converter_for_marshal_string(args_marshal: string/*ArgsMarshalString*/): Converter { - const steps = []; - let size = 0; - let is_result_definitely_unmarshaled = false, - is_result_possibly_unmarshaled = false, - result_unmarshaled_if_argc = -1, - needs_root_buffer = false; - - for (let i = 0; i < args_marshal.length; ++i) { - const key = args_marshal[i]; - - if (i === args_marshal.length - 1) { - if (key === "!") { - is_result_definitely_unmarshaled = true; - continue; - } else if (key === "m") { - is_result_possibly_unmarshaled = true; - result_unmarshaled_if_argc = args_marshal.length - 1; - } - } else if (key === "!") - throw new Error("! must be at the end of the signature"); - - const conv = primitiveConverters.get(key); - if (!conv) - throw new Error("Unknown parameter type " + key); - - const localStep = Object.create(conv.steps[0]); - localStep.size = conv.size; - if (conv.needs_root) - needs_root_buffer = true; - localStep.needs_root = conv.needs_root; - localStep.key = key; - steps.push(localStep); - size += conv.size; - } - - return { - steps, size, args_marshal, - is_result_definitely_unmarshaled, - is_result_possibly_unmarshaled, - result_unmarshaled_if_argc, - needs_root_buffer - }; -} - -function _get_converter_for_marshal_string(args_marshal: string/*ArgsMarshalString*/): Converter { - let converter = _signature_converters.get(args_marshal); - if (!converter) { - converter = _create_converter_for_marshal_string(args_marshal); - _signature_converters.set(args_marshal, converter); - } - - return converter; -} - -function _compile_converter_for_marshal_string(args_marshal: string/*ArgsMarshalString*/): Converter { - const converter = _get_converter_for_marshal_string(args_marshal); - if (typeof (converter.args_marshal) !== "string") - throw new Error("Corrupt converter for '" + args_marshal + "'"); - - if (converter.compiled_function && converter.compiled_variadic_function) - return converter; - - const converterName = args_marshal.replace("!", "_result_unmarshaled"); - converter.name = converterName; - - let body = []; - let argumentNames = ["method"]; - - const closure: any = { - Module, - setI32, - setU32, - setF32, - setF64, - setU52, - setI52, - setB32, - setI32_unchecked, - setU32_unchecked, - scratchValueRoot: converter.scratchValueRoot, - stackAlloc: Module.stackAlloc, - _zero_region - }; - let indirectLocalOffset = 0; - - // ensure the indirect values are 8-byte aligned so that aligned loads and stores will work - const indirectBaseOffset = ((((args_marshal.length * 4) + 7) / 8) | 0) * 8; - // worst-case allocation size instead of allocating dynamically, plus padding - // the padding is necessary to ensure that we don't overrun the buffer due to - // the 8-byte alignment we did above - const bufferSizeBytes = converter.size + (args_marshal.length * 4) + 16; - - body.push( - "if (!method) throw new Error('no method provided');", - `const buffer = stackAlloc(${bufferSizeBytes});`, - `_zero_region(buffer, ${bufferSizeBytes});`, - `const indirectStart = buffer + ${indirectBaseOffset};`, - "" - ); - - for (let i = 0; i < converter.steps.length; i++) { - const step = converter.steps[i]; - const closureKey = "step" + i; - const valueKey = "value" + i; - - const argKey = "arg" + i; - const offsetText = `(indirectStart + ${indirectLocalOffset})`; - argumentNames.push(argKey); - - if (step.convert_root) { - mono_assert(!step.indirect, "converter step cannot both be rooted and indirect"); - if (!converter.scratchValueRoot) { - // HACK: new_external_root rightly won't accept a null address - const dummyAddress = Module.stackSave(); - converter.scratchValueRoot = mono_wasm_new_external_root(dummyAddress); - closure.scratchValueRoot = converter.scratchValueRoot; - } - - closure[closureKey] = step.convert_root; - // Update our scratch external root to point to the indirect slot where our - // managed pointer is destined to live - body.push(`scratchValueRoot._set_address(${offsetText});`); - // Convert the object and store the managed reference through our scratch external root - body.push(`${closureKey}(${argKey}, scratchValueRoot);`); - if (step.byref) { - // for T&& we pass the address of the pointer stored on the stack - body.push(`let ${valueKey} = ${offsetText};`); - } else { - // It is safe to pass the pointer by value now since we know it is pinned - body.push(`let ${valueKey} = scratchValueRoot.value;`); - } - } else if (step.convert) { - closure[closureKey] = step.convert; - body.push(`let ${valueKey} = ${closureKey}(${argKey}, method, ${i});`); - } else { - body.push(`let ${valueKey} = ${argKey};`); - } - - if (step.needs_root && !step.convert_root) { - body.push("if (!rootBuffer) throw new Error('no root buffer provided');"); - body.push(`rootBuffer.set (${i}, ${valueKey});`); - } - - if (step.indirect) { - switch (step.indirect) { - case "bool": - body.push(`setB32(${offsetText}, ${valueKey});`); - break; - case "u32": - body.push(`setU32(${offsetText}, ${valueKey});`); - break; - case "i32": - body.push(`setI32(${offsetText}, ${valueKey});`); - break; - case "float": - body.push(`setF32(${offsetText}, ${valueKey});`); - break; - case "double": - body.push(`setF64(${offsetText}, ${valueKey});`); - break; - case "i52": - body.push(`setI52(${offsetText}, ${valueKey});`); - break; - case "u52": - body.push(`setU52(${offsetText}, ${valueKey});`); - break; - default: - throw new Error("Unimplemented indirect type: " + step.indirect); - } - - body.push(`setU32_unchecked(buffer + (${i} * 4), ${offsetText});`); - indirectLocalOffset += step.size!; - } else { - body.push(`setU32_unchecked(buffer + (${i} * 4), ${valueKey});`); - indirectLocalOffset += 4; - } - body.push(""); - } - - body.push("return buffer;"); - - let bodyJs = body.join("\r\n"), compiledFunction = null, compiledVariadicFunction = null; - try { - compiledFunction = _create_named_function("converter_" + converterName, argumentNames, bodyJs, closure); - converter.compiled_function = compiledFunction; - } catch (exc) { - converter.compiled_function = null; - mono_log_warn("compiling converter failed for", bodyJs, "with error", exc); - throw exc; - } - - - argumentNames = ["method", "args"]; - const variadicClosure = { - converter: compiledFunction - }; - body = [ - "return converter(", - " method," - ]; - - for (let i = 0; i < converter.steps.length; i++) { - body.push( - " args[" + i + - ( - (i == converter.steps.length - 1) - ? "]" - : "], " - ) - ); - } - - body.push(");"); - - bodyJs = body.join("\r\n"); - try { - compiledVariadicFunction = _create_named_function("variadic_converter_" + converterName, argumentNames, bodyJs, variadicClosure); - converter.compiled_variadic_function = compiledVariadicFunction; - } catch (exc) { - converter.compiled_variadic_function = null; - mono_log_warn("compiling converter failed for", bodyJs, "with error", exc); - throw exc; - } - - converter.scratchRootBuffer = null; - converter.scratchBuffer = VoidPtrNull; - - return converter; -} - -function _maybe_produce_signature_warning(converter: Converter) { - if (converter.has_warned_about_signature) - return; - - mono_log_warn("Deprecated raw return value signature: '" + converter.args_marshal + "'. End the signature with '!' instead of 'm'."); - converter.has_warned_about_signature = true; -} - -export function _decide_if_result_is_marshaled(converter: Converter, argc: number): boolean { - if (!converter) - return true; - - if ( - converter.is_result_possibly_unmarshaled && - (argc === converter.result_unmarshaled_if_argc) - ) { - if (argc < converter.result_unmarshaled_if_argc) - throw new Error(`Expected >= ${converter.result_unmarshaled_if_argc} argument(s) but got ${argc} for signature '${converter.args_marshal}'`); - - _maybe_produce_signature_warning(converter); - return false; - } else { - if (argc < converter.steps.length) - throw new Error(`Expected ${converter.steps.length} argument(s) but got ${argc} for signature '${converter.args_marshal}'`); - - return !converter.is_result_definitely_unmarshaled; - } -} - - -export function mono_bind_method(method: MonoMethod, args_marshal: string/*ArgsMarshalString*/, has_this_arg: boolean, friendly_name?: string): Function { - assert_legacy_interop(); - if (typeof (args_marshal) !== "string") - throw new Error("args_marshal argument invalid, expected string"); - - const key = `managed_${method}_${args_marshal}`; - let result = boundMethodsByMethod.get(key); - if (result) { - return result; - } - if (!friendly_name) { - friendly_name = key; - } - - let converter: Converter | null = null; - if (typeof (args_marshal) === "string") { - converter = _compile_converter_for_marshal_string(args_marshal); - } - - // FIXME - const unbox_buffer_size = 128; - const unbox_buffer = Module._malloc(unbox_buffer_size); - - const token: BoundMethodToken = { - method, - converter, - scratchRootBuffer: null, - scratchBuffer: VoidPtrNull, - scratchResultRoot: mono_wasm_new_root(), - scratchExceptionRoot: mono_wasm_new_root(), - scratchThisArgRoot: mono_wasm_new_root() - }; - const closure: any = { - Module, - mono_wasm_new_root, - get_js_owned_object_by_gc_handle_ref, - _create_temp_frame, - _handle_exception_for_call, - _teardown_after_call, - mono_wasm_try_unbox_primitive_and_get_type_ref: cwraps.mono_wasm_try_unbox_primitive_and_get_type_ref, - _unbox_mono_obj_root_with_known_nonprimitive_type, - invoke_method_ref: cwraps.mono_wasm_invoke_method_ref, - method, - token, - unbox_buffer, - unbox_buffer_size, - getB32, - getI32, - getU32, - getF32, - getF64, - stackSave: Module.stackSave - }; - - const converterKey = converter ? "converter_" + converter.name : ""; - if (converter) - closure[converterKey] = converter; - - const argumentNames = []; - const body = [ - "_create_temp_frame();", - "let resultRoot = token.scratchResultRoot, exceptionRoot = token.scratchExceptionRoot, thisArgRoot = token.scratchThisArgRoot , sp = stackSave();", - "token.scratchResultRoot = null;", - "token.scratchExceptionRoot = null;", - "token.scratchThisArgRoot = null;", - "if (resultRoot === null)", - " resultRoot = mono_wasm_new_root ();", - "if (exceptionRoot === null)", - " exceptionRoot = mono_wasm_new_root ();", - "if (thisArgRoot === null)", - " thisArgRoot = mono_wasm_new_root ();", - "" - ]; - - if (converter) { - body.push( - `let buffer = ${converterKey}.compiled_function(`, - " method," - ); - - for (let i = 0; i < converter.steps.length; i++) { - const argName = "arg" + i; - argumentNames.push(argName); - body.push( - " " + argName + - ( - (i == converter.steps.length - 1) - ? "" - : ", " - ) - ); - } - - body.push(");"); - - } else { - body.push("let buffer = 0;"); - } - - if (converter && converter.is_result_definitely_unmarshaled) { - body.push("let is_result_marshaled = false;"); - } else if (converter && converter.is_result_possibly_unmarshaled) { - body.push(`let is_result_marshaled = arguments.length !== ${converter.result_unmarshaled_if_argc};`); - } else { - body.push("let is_result_marshaled = true;"); - } - - // We inline a bunch of the invoke and marshaling logic here in order to eliminate the GC pressure normally - // created by the unboxing part of the call process. Because unbox_mono_obj(_root) can return non-numeric - // types, v8 and spidermonkey allocate and store its result on the heap (in the nursery, to be fair). - // For a bound method however, we know the result will always be the same type because C# methods have known - // return types. Inlining the invoke and marshaling logic means that even though the bound method has logic - // for handling various types, only one path through the method (for its appropriate return type) will ever - // be taken, and the JIT will see that the 'result' local and thus the return value of this function are - // always of the exact same type. All of the branches related to this end up being predicted and low-cost. - // The end result is that bound method invocations don't always allocate, so no more nursery GCs. Yay! -kg - body.push( - "", - "", - "", - ); - if (has_this_arg) { - body.push("get_js_owned_object_by_gc_handle_ref(this.this_arg_gc_handle, thisArgRoot.address);"); - body.push("invoke_method_ref (method, thisArgRoot.address, buffer, exceptionRoot.address, resultRoot.address);"); - } else { - body.push("invoke_method_ref (method, 0, buffer, exceptionRoot.address, resultRoot.address);"); - } - - body.push( - `_handle_exception_for_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, thisArgRoot, sp);`, - "", - "let resultPtr = resultRoot.value, result = undefined;" - ); - - if (converter) { - if (converter.is_result_possibly_unmarshaled) - body.push("if (!is_result_marshaled) "); - - if (converter.is_result_definitely_unmarshaled || converter.is_result_possibly_unmarshaled) - body.push(" result = resultPtr;"); - - if (!converter.is_result_definitely_unmarshaled) - body.push( - "if (is_result_marshaled) {", - // For the common scenario where the return type is a primitive, we want to try and unbox it directly - // into our existing heap allocation and then read it out of the heap. Doing this all in one operation - // means that we only need to enter a gc safe region twice (instead of 3+ times with the normal, - // slower check-type-and-then-unbox flow which has extra checks since unbox verifies the type). - " let resultType = mono_wasm_try_unbox_primitive_and_get_type_ref (resultRoot.address, unbox_buffer, unbox_buffer_size);", - " switch (resultType) {", - ` case ${MarshalType.INT}:`, - " result = getI32(unbox_buffer); break;", - ` case ${MarshalType.POINTER}:`, // FIXME: Is this right? - ` case ${MarshalType.UINT32}:`, - " result = getU32(unbox_buffer); break;", - ` case ${MarshalType.FP32}:`, - " result = getF32(unbox_buffer); break;", - ` case ${MarshalType.FP64}:`, - " result = getF64(unbox_buffer); break;", - ` case ${MarshalType.BOOL}:`, - " result = getB32(unbox_buffer); break;", - ` case ${MarshalType.CHAR}:`, - " result = String.fromCharCode(getI32(unbox_buffer)); break;", - ` case ${MarshalType.NULL}:`, - " result = null; break;", - " default:", - " result = _unbox_mono_obj_root_with_known_nonprimitive_type (resultRoot, resultType, unbox_buffer); break;", - " }", - "}" - ); - } else { - throw new Error("No converter"); - } - - let displayName = friendly_name.replace(escapeRE, "_"); - - if (has_this_arg) - displayName += "_this"; - - body.push( - `_teardown_after_call (${converterKey}, token, buffer, resultRoot, exceptionRoot, thisArgRoot, sp);`, - "return result;" - ); - - const bodyJs = body.join("\r\n"); - - result = _create_named_function(displayName, argumentNames, bodyJs, closure); - boundMethodsByMethod.set(key, result); - - return result; -} - -/* -We currently don't use these types because it makes typeScript compiler very slow. - -declare const enum ArgsMarshal { - Int32 = "i", // int32 - Int32Enum = "j", // int32 - Enum with underlying type of int32 - Int64 = "l", // int64 - Int64Enum = "k", // int64 - Enum with underlying type of int64 - Float32 = "f", // float - Float64 = "d", // double - String = "s", // string - Char = "s", // interned string - JSObj = "o", // js object will be converted to a C# object (this will box numbers/bool/promises) - MONOObj = "m", // raw mono object. Don't use it unless you know what you're doing -} - -// to suppress marshaling of the return value, place '!' at the end of args_marshal, i.e. 'ii!' instead of 'ii' -type _ExtraArgsMarshalOperators = "!" | ""; - -export type ArgsMarshalString = "" - | `${ArgsMarshal}${_ExtraArgsMarshalOperators}` - | `${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}` - | `${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}` - | `${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}`; -*/ - -type ConverterStepIndirects = "u32" | "i32" | "float" | "double" | "u52" | "i52" | "reference" | "bool" -type VariadicConverterFunction = (method: MonoMethod, ...args: unknown[]) => VoidPtr; -type ConverterFunction = (method: MonoMethod /* , ... */) => VoidPtr; - -export type Converter = { - steps: { - // (value: any, method: MonoMethod, arg_index: int) - convert?: boolean | Function; - // (value: any, result_root: WasmRoot) - convert_root?: Function; - needs_root?: boolean; - byref?: boolean; - indirect?: ConverterStepIndirects; - size?: number; - }[]; - size: number; - args_marshal?: string/*ArgsMarshalString*/; - is_result_definitely_unmarshaled?: boolean; - is_result_possibly_unmarshaled?: boolean; - result_unmarshaled_if_argc?: number; - needs_root_buffer?: boolean; - key?: string; - name?: string; - needs_root?: boolean; - compiled_variadic_function?: VariadicConverterFunction | null; - compiled_function?: ConverterFunction | null; - scratchRootBuffer?: WasmRootBuffer | null; - scratchBuffer?: VoidPtr; - scratchValueRoot?: WasmRoot; - has_warned_about_signature?: boolean; - convert?: Function | null; - method?: MonoMethod | null; -} - -export type BoundMethodToken = { - method: MonoMethod; - converter: Converter | null; - scratchRootBuffer: WasmRootBuffer | null; - scratchBuffer: VoidPtr; - scratchResultRoot: WasmRoot; - scratchExceptionRoot: WasmRoot; - scratchThisArgRoot: WasmRoot; -} - -function _handle_exception_for_call( - converter: Converter | undefined, token: BoundMethodToken | null, - buffer: VoidPtr, resultRoot: WasmRoot, - exceptionRoot: WasmRoot, - thisArgRoot: WasmRoot, - sp: VoidPtr -): void { - const exc = _convert_exception_for_method_call(resultRoot, exceptionRoot); - if (!exc) - return; - - _teardown_after_call(converter, token, buffer, resultRoot, exceptionRoot, thisArgRoot, sp); - throw exc; -} - -function _convert_exception_for_method_call(result: WasmRoot, exception: WasmRoot) { - if (exception.value === MonoObjectNull) - return null; - - const msg = monoStringToString(result); - const err = new Error(msg!); //the convention is that invoke_method ToString () any outgoing exception - // console.warn (`error ${msg} at location ${err.stack}); - return err; -} - -export function mono_method_resolve(fqn: string): MonoMethod { - const { assembly, namespace, classname, methodname } = parseFQN(fqn); - - const asm = cwraps.mono_wasm_assembly_load(assembly); - if (!asm) - throw new Error("Could not find assembly: " + assembly); - - const klass = cwraps.mono_wasm_assembly_find_class(asm, namespace, classname); - if (!klass) - throw new Error("Could not find class: " + namespace + ":" + classname + " in assembly " + assembly); - - const method = cwraps.mono_wasm_assembly_find_method(klass, methodname, -1); - if (!method) - throw new Error("Could not find method: " + methodname); - return method; -} - -export function mono_method_get_call_signature_ref(method: MonoMethod, mono_obj?: WasmRoot): string/*ArgsMarshalString*/ { - return legacyManagedExports._get_call_sig_ref(method, mono_obj ? mono_obj.address : legacyHelpers._null_root.address); -} - -export function assert_legacy_interop(): void { - if (MonoWasmThreads) { - mono_assert(!ENVIRONMENT_IS_PTHREAD, "Legacy interop is not supported with WebAssembly threads."); - } - assert_js_interop(); -} \ No newline at end of file diff --git a/src/mono/browser/runtime/net6-legacy/method-calls.ts b/src/mono/browser/runtime/net6-legacy/method-calls.ts deleted file mode 100644 index a2033b256f0000..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/method-calls.ts +++ /dev/null @@ -1,307 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { mono_wasm_get_jsobj_from_js_handle } from "../gc-handles"; -import { Module, INTERNAL, loaderHelpers } from "../globals"; -import { wrap_error_root, wrap_no_error_root } from "../invoke-js"; -import { _release_temp_frame } from "../memory"; -import { mono_wasm_new_external_root, mono_wasm_new_root } from "../roots"; -import { find_entry_point } from "../run"; -import { monoStringToString, stringToMonoStringRoot } from "../strings"; -import { JSHandle, MonoStringRef, MonoObjectRef, MonoArray, MonoString, MonoObject, is_nullish, WasmRoot } from "../types/internal"; -import { Int32Ptr, VoidPtr } from "../types/emscripten"; -import { mono_array_root_to_js_array, unbox_mono_obj_root } from "./cs-to-js"; -import { js_array_to_mono_array, js_to_mono_obj_root } from "./js-to-cs"; -import { Converter, BoundMethodToken, mono_method_resolve, mono_method_get_call_signature_ref, mono_bind_method, assert_legacy_interop } from "./method-binding"; - -const boundMethodsByFqn: Map = new Map(); - -export function _teardown_after_call( - converter: Converter | undefined, token: BoundMethodToken | null, - buffer: VoidPtr, - resultRoot: WasmRoot, - exceptionRoot: WasmRoot, - thisArgRoot: WasmRoot, - sp: VoidPtr -): void { - _release_temp_frame(); - Module.stackRestore(sp); - - if (typeof (resultRoot) === "object") { - resultRoot.clear(); - if ((token !== null) && (token.scratchResultRoot === null)) - token.scratchResultRoot = resultRoot; - else - resultRoot.release(); - } - if (typeof (exceptionRoot) === "object") { - exceptionRoot.clear(); - if ((token !== null) && (token.scratchExceptionRoot === null)) - token.scratchExceptionRoot = exceptionRoot; - else - exceptionRoot.release(); - } - if (typeof (thisArgRoot) === "object") { - thisArgRoot.clear(); - if ((token !== null) && (token.scratchThisArgRoot === null)) - token.scratchThisArgRoot = thisArgRoot; - else - thisArgRoot.release(); - } -} - -export function mono_bind_static_method(fqn: string, signature?: string/*ArgsMarshalString*/): Function { - assert_legacy_interop(); - - const key = `${fqn}-${signature}`; - let js_method = boundMethodsByFqn.get(key); - if (js_method === undefined) { - const method = mono_method_resolve(fqn); - - if (typeof signature === "undefined") - signature = mono_method_get_call_signature_ref(method, undefined); - - js_method = mono_bind_method(method, signature!, false, fqn); - boundMethodsByFqn.set(key, js_method); - } - return js_method; -} - -export function mono_bind_assembly_entry_point(assembly: string, signature?: string/*ArgsMarshalString*/): Function { - assert_legacy_interop(); - const method = find_entry_point(assembly); - if (typeof (signature) !== "string") - signature = mono_method_get_call_signature_ref(method, undefined); - - const js_method = mono_bind_method(method, signature!, false, "_" + assembly + "__entrypoint"); - - return async function (...args: any[]) { - loaderHelpers.assert_runtime_running(); - if (args.length > 0 && Array.isArray(args[0])) - args[0] = js_array_to_mono_array(args[0], true, false); - return js_method(...args); - }; -} - -export function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: string/*ArgsMarshalString*/): number { - assert_legacy_interop(); - if (!args) { - args = [[]]; - } - return mono_bind_assembly_entry_point(assembly, signature)(...args); -} - -export function mono_wasm_invoke_js_with_args_ref(js_handle: JSHandle, method_name: MonoStringRef, args: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): any { - assert_legacy_interop(); - const argsRoot = mono_wasm_new_external_root(args), - nameRoot = mono_wasm_new_external_root(method_name), - resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_name = monoStringToString(nameRoot); - if (!js_name || (typeof (js_name) !== "string")) { - wrap_error_root(is_exception, "ERR12: Invalid method name object @" + nameRoot.value, resultRoot); - return; - } - - const obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(obj)) { - wrap_error_root(is_exception, "ERR13: Invalid JS object handle '" + js_handle + "' while invoking '" + js_name + "'", resultRoot); - return; - } - - const js_args = mono_array_root_to_js_array(argsRoot); - - try { - const m = obj[js_name]; - if (typeof m === "undefined") - throw new Error("Method: '" + js_name + "' not found for: '" + Object.prototype.toString.call(obj) + "'"); - const res = m.apply(obj, js_args); - - js_to_mono_obj_root(res, resultRoot, true); - wrap_no_error_root(is_exception); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - } - } finally { - argsRoot.release(); - nameRoot.release(); - resultRoot.release(); - } -} - -export function mono_wasm_get_object_property_ref(js_handle: JSHandle, property_name: MonoStringRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - assert_legacy_interop(); - const nameRoot = mono_wasm_new_external_root(property_name), - resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_name = monoStringToString(nameRoot); - if (!js_name) { - wrap_error_root(is_exception, "Invalid property name object '" + nameRoot.value + "'", resultRoot); - return; - } - - const obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(obj)) { - wrap_error_root(is_exception, "ERR01: Invalid JS object handle '" + js_handle + "' while geting '" + js_name + "'", resultRoot); - return; - } - - const m = obj[js_name]; - js_to_mono_obj_root(m, resultRoot, true); - wrap_no_error_root(is_exception); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - } finally { - resultRoot.release(); - nameRoot.release(); - } -} - -export function mono_wasm_set_object_property_ref(js_handle: JSHandle, property_name: MonoStringRef, value: MonoObjectRef, createIfNotExist: boolean, hasOwnProperty: boolean, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - assert_legacy_interop(); - const valueRoot = mono_wasm_new_external_root(value), - nameRoot = mono_wasm_new_external_root(property_name), - resultRoot = mono_wasm_new_external_root(result_address); - try { - - const property = monoStringToString(nameRoot); - if (!property) { - wrap_error_root(is_exception, "Invalid property name object '" + property_name + "'", resultRoot); - return; - } - - const js_obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(js_obj)) { - wrap_error_root(is_exception, "ERR02: Invalid JS object handle '" + js_handle + "' while setting '" + property + "'", resultRoot); - return; - } - - const js_value = unbox_mono_obj_root(valueRoot); - - if (createIfNotExist) { - js_obj[property] = js_value; - } - else { - if (!createIfNotExist) { - if (!Object.prototype.hasOwnProperty.call(js_obj, property)) { - return; - } - } - if (hasOwnProperty === true) { - if (Object.prototype.hasOwnProperty.call(js_obj, property)) { - js_obj[property] = js_value; - } - } - else { - js_obj[property] = js_value; - } - } - wrap_no_error_root(is_exception, resultRoot); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - } finally { - resultRoot.release(); - nameRoot.release(); - valueRoot.release(); - } -} - -export function mono_wasm_get_by_index_ref(js_handle: JSHandle, property_index: number, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - assert_legacy_interop(); - const resultRoot = mono_wasm_new_external_root(result_address); - try { - const obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(obj)) { - wrap_error_root(is_exception, "ERR03: Invalid JS object handle '" + js_handle + "' while getting [" + property_index + "]", resultRoot); - return; - } - - const m = obj[property_index]; - js_to_mono_obj_root(m, resultRoot, true); - wrap_no_error_root(is_exception); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - } finally { - resultRoot.release(); - } -} - -export function mono_wasm_set_by_index_ref(js_handle: JSHandle, property_index: number, value: MonoObjectRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - assert_legacy_interop(); - const valueRoot = mono_wasm_new_external_root(value), - resultRoot = mono_wasm_new_external_root(result_address); - try { - const obj = mono_wasm_get_jsobj_from_js_handle(js_handle); - if (is_nullish(obj)) { - wrap_error_root(is_exception, "ERR04: Invalid JS object handle '" + js_handle + "' while setting [" + property_index + "]", resultRoot); - return; - } - - const js_value = unbox_mono_obj_root(valueRoot); - obj[property_index] = js_value; - wrap_no_error_root(is_exception, resultRoot); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - } finally { - resultRoot.release(); - valueRoot.release(); - } -} - -export function mono_wasm_get_global_object_ref(global_name: MonoStringRef, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - assert_legacy_interop(); - const nameRoot = mono_wasm_new_external_root(global_name), - resultRoot = mono_wasm_new_external_root(result_address); - try { - const js_name = monoStringToString(nameRoot); - - let globalObj; - - if (!js_name) { - globalObj = globalThis; - } - else if (js_name == "Module") { - globalObj = Module; - } - else if (js_name == "INTERNAL") { - globalObj = INTERNAL; - } - else { - globalObj = (globalThis)[js_name]; - } - - // TODO returning null may be useful when probing for browser features - if (globalObj === null || typeof globalObj === undefined) { - wrap_error_root(is_exception, "Global object '" + js_name + "' not found.", resultRoot); - return; - } - - js_to_mono_obj_root(globalObj, resultRoot, true); - wrap_no_error_root(is_exception); - } catch (ex) { - wrap_error_root(is_exception, ex, resultRoot); - } finally { - resultRoot.release(); - nameRoot.release(); - } -} - -// Blazor specific custom routine -export function mono_wasm_invoke_js_blazor(exceptionMessage: Int32Ptr, callInfo: any, arg0: any, arg1: any, arg2: any): void | number { - try { - assert_legacy_interop(); - const blazorExports = (globalThis).Blazor; - if (!blazorExports) { - throw new Error("The blazor.webassembly.js library is not loaded."); - } - - return blazorExports._internal.invokeJSFromDotNet(callInfo, arg0, arg1, arg2); - } catch (ex: any) { - const exceptionJsString = ex.message + "\n" + ex.stack; - const exceptionRoot = mono_wasm_new_root(); - stringToMonoStringRoot(exceptionJsString, exceptionRoot); - exceptionRoot.copy_to_address(exceptionMessage); - exceptionRoot.release(); - return 0; - } -} diff --git a/src/mono/browser/runtime/net6-legacy/strings.ts b/src/mono/browser/runtime/net6-legacy/strings.ts deleted file mode 100644 index f651100c672902..00000000000000 --- a/src/mono/browser/runtime/net6-legacy/strings.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { mono_assert } from "../globals"; -import { mono_wasm_new_root } from "../roots"; -import { interned_string_table, mono_wasm_empty_string, stringToInternedMonoStringRoot, stringToMonoStringRoot } from "../strings"; -import { MonoString, is_nullish } from "../types/internal"; - -import { assert_legacy_interop } from "./method-binding"; - -/** - * @deprecated Not GC or thread safe - */ -export function stringToMonoStringUnsafe(string: string): MonoString { - assert_legacy_interop(); - const temp = mono_wasm_new_root(); - try { - stringToMonoStringRoot(string, temp); - return temp.value; - } finally { - temp.release(); - } -} - -// this is only used in legacy unit tests -export function stringToMonoStringIntern(string: string): string { - if (string.length === 0) - return mono_wasm_empty_string; - - const root = mono_wasm_new_root(); - try { - stringToInternedMonoStringRoot(string, root); - const result = interned_string_table.get(root.value); - mono_assert(!is_nullish(result), "internal error: interned_string_table did not contain string after stringToMonoStringIntern"); - return result; - } - finally { - root.release(); - } -} diff --git a/src/mono/browser/runtime/rollup.config.js b/src/mono/browser/runtime/rollup.config.js index 4c1622e8f829fb..769d6a01518ae2 100644 --- a/src/mono/browser/runtime/rollup.config.js +++ b/src/mono/browser/runtime/rollup.config.js @@ -22,11 +22,10 @@ const wasmObjDir = process.env.WasmObjDir ? process.env.WasmObjDir.replace(/"/g, const monoWasmThreads = process.env.MonoWasmThreads === "true" ? true : false; const wasmEnableSIMD = process.env.WASM_ENABLE_SIMD === "1" ? true : false; const wasmEnableExceptionHandling = process.env.WASM_ENABLE_EH === "1" ? true : false; -const wasmEnableLegacyJsInterop = process.env.DISABLE_LEGACY_JS_INTEROP !== "1" ? true : false; const wasmEnableJsInteropByValue = process.env.ENABLE_JS_INTEROP_BY_VALUE == "1" ? true : false; const monoDiagnosticsMock = process.env.MonoDiagnosticsMock === "true" ? true : false; // because of stack walk at src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs -// and unit test at src\libraries\System.Runtime.InteropServices.JavaScript\tests\System.Runtime.InteropServices.JavaScript.Legacy.UnitTests\timers.mjs +// and unit test at with timers.mjs const keep_fnames = /(mono_wasm_runtime_ready|mono_wasm_fire_debugger_agent_message_with_data|mono_wasm_fire_debugger_agent_message_with_data_to_pause|mono_wasm_schedule_timer_tick)/; const keep_classnames = /(ManagedObject|ManagedError|Span|ArraySegment|WasmRootBuffer|SessionOptionsBuilder)/; const terserConfig = { @@ -103,7 +102,6 @@ const envConstants = { wasmEnableExceptionHandling, monoDiagnosticsMock, gitHash, - wasmEnableLegacyJsInterop, wasmEnableJsInteropByValue, isContinuousIntegrationBuild, }; @@ -215,20 +213,6 @@ const typesConfig = { external: externalDependencies, plugins: [dts()], }; -const legacyTypesConfig = { - input: "./net6-legacy/export-types.ts", - output: [ - { - format: "es", - file: nativeBinDir + "/dotnet-legacy.d.ts", - banner: banner_dts, - plugins: [writeOnChangePlugin()], - } - ], - external: externalDependencies, - plugins: [dts()], -}; - let diagnosticMockTypesConfig = undefined; @@ -241,12 +225,6 @@ if (isDebug) { banner: banner_dts, plugins: [alwaysLF(), writeOnChangePlugin()], }); - legacyTypesConfig.output.push({ - format: "es", - file: "./dotnet-legacy.d.ts", - banner: banner_dts, - plugins: [alwaysLF(), writeOnChangePlugin()], - }); // export types into the source code and commit to git diagnosticMockTypesConfig = { @@ -289,7 +267,6 @@ const allConfigs = [ runtimeConfig, wasmImportsConfig, typesConfig, - legacyTypesConfig, ].concat(workerConfigs) .concat(diagnosticMockTypesConfig ? [diagnosticMockTypesConfig] : []); export default defineConfig(allConfigs); diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 8ba863664e9d9f..55088f67fe72a0 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. import MonoWasmThreads from "consts:monoWasmThreads"; -import WasmEnableLegacyJsInterop from "consts:wasmEnableLegacyJsInterop"; import { DotnetModuleInternal, CharPtrNull } from "./types/internal"; -import { linkerDisableLegacyJsInterop, ENVIRONMENT_IS_PTHREAD, ENVIRONMENT_IS_NODE, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert, linkerWasmEnableSIMD, linkerWasmEnableEH, ENVIRONMENT_IS_WORKER } from "./globals"; +import { ENVIRONMENT_IS_NODE, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert, linkerWasmEnableSIMD, linkerWasmEnableEH, ENVIRONMENT_IS_WORKER } from "./globals"; import cwraps, { init_c_exports, threads_c_functions as tcwraps } from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; import { toBase64StringImpl } from "./base64"; @@ -30,11 +29,6 @@ import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from " import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents } from "./pthreads/worker"; import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; import { jiterpreter_allocate_tables } from "./jiterpreter-support"; - -// legacy -import { init_legacy_exports } from "./net6-legacy/corebindings"; -import { cwraps_binding_api, cwraps_mono_api } from "./net6-legacy/exports-legacy"; -import { BINDING, MONO } from "./net6-legacy/globals"; import { localHeapViewU8 } from "./memory"; import { assertNoProxies } from "./gc-handles"; @@ -382,10 +376,6 @@ function mono_wasm_pre_init_essential(isWorker: boolean): void { init_c_exports(); cwraps_internal(INTERNAL); - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { - cwraps_mono_api(MONO); - cwraps_binding_api(BINDING); - } // removeRunDependency triggers the dependenciesFulfilled callback (runCaller) in // emscripten - on a worker since we don't have any other dependencies that causes run() to get // called too soon; and then it will get called a second time when dotnet.native.js calls it directly. @@ -409,24 +399,6 @@ async function mono_wasm_pre_init_essential_async(): Promise { async function mono_wasm_after_user_runtime_initialized(): Promise { mono_log_debug("mono_wasm_after_user_runtime_initialized"); try { - if (!Module.disableDotnet6Compatibility && Module.exports) { - // Export emscripten defined in module through EXPORTED_RUNTIME_METHODS - // Useful to export IDBFS or other similar types generally exposed as - // global types when emscripten is not modularized. - const globalThisAny = globalThis as any; - for (let i = 0; i < Module.exports.length; ++i) { - const exportName = Module.exports[i]; - const exportValue = (Module)[exportName]; - - if (exportValue != undefined) { - globalThisAny[exportName] = exportValue; - } - else { - mono_log_warn(`The exported symbol ${exportName} could not be found in the emscripten module`); - } - } - } - mono_log_debug("Initializing mono runtime"); if (Module.onDotnetReady) { @@ -606,9 +578,6 @@ export function bindings_init(): void { const mark = startMeasure(); strings_init(); init_managed_exports(); - if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop && !ENVIRONMENT_IS_PTHREAD) { - init_legacy_exports(); - } initialize_marshalers_to_js(); initialize_marshalers_to_cs(); runtimeHelpers._i52_error_scratch_buffer = Module._malloc(4); diff --git a/src/mono/browser/runtime/tsconfig.shared.json b/src/mono/browser/runtime/tsconfig.shared.json index 94b120645d1d19..7515efada53d74 100644 --- a/src/mono/browser/runtime/tsconfig.shared.json +++ b/src/mono/browser/runtime/tsconfig.shared.json @@ -16,7 +16,6 @@ }, "exclude": [ "dotnet.d.ts", - "dotnet-legacy.d.ts", "diagnostics-mock.d.ts", "bin" ] diff --git a/src/mono/browser/runtime/types/emscripten.ts b/src/mono/browser/runtime/types/emscripten.ts index 66ad8bb5aad01c..a9691cde490611 100644 --- a/src/mono/browser/runtime/types/emscripten.ts +++ b/src/mono/browser/runtime/types/emscripten.ts @@ -23,25 +23,6 @@ export declare interface CharPtrPtr extends NativePointer { } export declare interface EmscriptenModule { - /** @deprecated Please use localHeapViewI8() instead.*/ - HEAP8: Int8Array, - /** @deprecated Please use localHeapViewI16() instead.*/ - HEAP16: Int16Array; - /** @deprecated Please use localHeapViewI32() instead. */ - HEAP32: Int32Array; - /** @deprecated Please use localHeapViewI64() instead. */ - HEAP64: BigInt64Array; - /** @deprecated Please use localHeapViewU8() instead. */ - HEAPU8: Uint8Array; - /** @deprecated Please use localHeapViewU16() instead. */ - HEAPU16: Uint16Array; - /** @deprecated Please use localHeapViewU32() instead */ - HEAPU32: Uint32Array; - /** @deprecated Please use localHeapViewF32() instead */ - HEAPF32: Float32Array; - /** @deprecated Please use localHeapViewF64() instead. */ - HEAPF64: Float64Array; - // this should match emcc -s EXPORTED_FUNCTIONS _malloc(size: number): VoidPtr; _free(ptr: VoidPtr): void; diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts index c47d4bd2bd72a0..13e71c1fc240eb 100644 --- a/src/mono/browser/runtime/types/index.ts +++ b/src/mono/browser/runtime/types/index.ts @@ -329,8 +329,6 @@ export const enum GlobalizationMode { } export type DotnetModuleConfig = { - disableDotnet6Compatibility?: boolean, - config?: MonoConfig, configSrc?: string, onConfigLoaded?: (config: MonoConfig) => void | Promise; @@ -387,14 +385,6 @@ export type APIType = { } export type RuntimeAPI = { - /** - * @deprecated Please use API object instead. See also MONOType in dotnet-legacy.d.ts - */ - MONO: any, - /** - * @deprecated Please use API object instead. See also BINDINGType in dotnet-legacy.d.ts - */ - BINDING: any, INTERNAL: any, Module: EmscriptenModule, runtimeId: number, diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index c97d4c98f0211b..00b02c60ad3964 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -284,7 +284,6 @@ export function is_nullish(value: T | null | undefined): value is null | unde export type EmscriptenInternals = { isPThread: boolean, - linkerDisableLegacyJsInterop: boolean, linkerWasmEnableSIMD: boolean, linkerWasmEnableEH: boolean, linkerEnableAotProfiler: boolean, @@ -453,6 +452,16 @@ export interface WasmRootBuffer { } export declare interface EmscriptenModuleInternal { + HEAP8: Int8Array, + HEAP16: Int16Array; + HEAP32: Int32Array; + HEAP64: BigInt64Array; + HEAPU8: Uint8Array; + HEAPU16: Uint16Array; + HEAPU32: Uint32Array; + HEAPF32: Float32Array; + HEAPF64: Float64Array; + __locateFile?: (path: string, prefix?: string) => string; locateFile?: (path: string, prefix?: string) => string; mainScriptUrlOrBlob?: string; diff --git a/src/mono/browser/runtime/wasm-config.h.in b/src/mono/browser/runtime/wasm-config.h.in index 044bac49598083..d8a2d8127c2ec3 100644 --- a/src/mono/browser/runtime/wasm-config.h.in +++ b/src/mono/browser/runtime/wasm-config.h.in @@ -7,9 +7,6 @@ /* Support for threads is disabled */ #cmakedefine DISABLE_THREADS -/* Support for legacy JS interop is disabled */ -#cmakedefine DISABLE_LEGACY_JS_INTEROP - /* Support for JS interop without pointers to managed objects */ #cmakedefine ENABLE_JS_INTEROP_BY_VALUE diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in index acbf0c5ecdaaf8..028b54fd5677f4 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in @@ -45,7 +45,6 @@ <_WasmPropertiesDifferFromRuntimePackThusNativeBuildNeeded Condition=" - '$(WasmEnableLegacyJsInterop)' == 'false' or '$(WasmEnableSIMD)' == 'false' or '$(WasmEnableExceptionHandling)' == 'false' or '$(InvariantTimezone)' == 'true' or diff --git a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj index a16c12098f18e5..fee308d1230f9d 100644 --- a/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj +++ b/src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj @@ -4,7 +4,6 @@ true true true - false false true true diff --git a/src/mono/sample/wasm/browser-bench/JSInterop.cs b/src/mono/sample/wasm/browser-bench/JSInterop.cs index 2c1830f39987a8..285491644a1a07 100644 --- a/src/mono/sample/wasm/browser-bench/JSInterop.cs +++ b/src/mono/sample/wasm/browser-bench/JSInterop.cs @@ -25,9 +25,7 @@ class JSInteropTask : BenchTask public JSInteropTask() { measurements = new Measurement[] { - new LegacyExportIntMeasurement(), new JSExportIntMeasurement(), - new LegacyExportStringMeasurement(), new JSExportStringMeasurement(), new JSImportIntMeasurement(), new JSImportStringMeasurement(), @@ -38,18 +36,6 @@ public JSInteropTask() }; } - public class LegacyExportIntMeasurement : BenchTask.Measurement - { - public override int InitialSamples => 3; - public override string Name => "LegacyExportInt"; - // because of the aggressive trimming of methods reachable via JS legacy bind_static_method - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "Sample.ImportsExportsHelper", Test.AssemblyName)] - public override void RunStep() - { - ImportsExportsHelper.RunLegacyExportInt(10000); - } - } - public class JSExportIntMeasurement : BenchTask.Measurement { public override int InitialSamples => 10; @@ -60,19 +46,6 @@ public override void RunStep() } } - public class LegacyExportStringMeasurement : BenchTask.Measurement - { - public override int InitialSamples => 3; - public override string Name => "LegacyExportString"; - - // because of the aggressive trimming of methods reachable via JS legacy bind_static_method - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "Sample.ImportsExportsHelper", Test.AssemblyName)] - public override void RunStep() - { - ImportsExportsHelper.RunLegacyExportString(10000); - } - } - public class JSExportStringMeasurement : BenchTask.Measurement { public override int InitialSamples => 3; @@ -187,15 +160,9 @@ public override void RunStep() partial class ImportsExportsHelper { - [JSImport("Sample.Test.runLegacyExportInt", "main.js")] - public static partial void RunLegacyExportInt(int count); - [JSImport("Sample.Test.runJSExportInt", "main.js")] public static partial void RunJSExportInt(int count); - [JSImport("Sample.Test.runLegacyExportString", "main.js")] - public static partial void RunLegacyExportString(int count); - [JSImport("Sample.Test.runJSExportString", "main.js")] public static partial void RunJSExportString(int count); @@ -211,12 +178,6 @@ partial class ImportsExportsHelper [JSImport("Sample.Test.importTargetThrows", "main.js")] public static partial void ImportTargetThrows(int value); - [MethodImpl(MethodImplOptions.NoInlining)] - public static int LegacyExportTargetInt(int value) - { - return value + 1; - } - [JSImport("Sample.Test.importTargetManyArgs", "main.js")] public static partial double ImportTargetManyArgs(int arg1, int arg2, string arg3, string arg4, IntPtr arg5, bool arg6, [JSMarshalAs] long arg7, int? arg8, double arg9, @@ -229,12 +190,6 @@ public static int JSExportTargetInt(int value) return value + 1; } - [MethodImpl(MethodImplOptions.NoInlining)] - public static string LegacyExportTargetString(string value) - { - return value + "A"; - } - [JSExport] public static string JSExportTargetString(string value) { diff --git a/src/mono/sample/wasm/browser-bench/main.js b/src/mono/sample/wasm/browser-bench/main.js index f0302ae1611640..da1d7255e7393b 100644 --- a/src/mono/sample/wasm/browser-bench/main.js +++ b/src/mono/sample/wasm/browser-bench/main.js @@ -9,30 +9,16 @@ let runBenchmark; let setTasks; let setExclusions; let getFullJsonResults; -let legacyExportTargetInt; let jsExportTargetInt; -let legacyExportTargetString; let jsExportTargetString; let _jiterpreter_dump_stats, _interp_pgo_save_data; -function runLegacyExportInt(count) { - for (let i = 0; i < count; i++) { - legacyExportTargetInt(i); - } -} - function runJSExportInt(count) { for (let i = 0; i < count; i++) { jsExportTargetInt(i); } } -function runLegacyExportString(count) { - for (let i = 0; i < count; i++) { - legacyExportTargetString("A" + i); - } -} - function runJSExportString(count) { for (let i = 0; i < count; i++) { jsExportTargetString("A" + i); @@ -61,7 +47,7 @@ function importTargetThrows(value) { } class MainApp { - async init({ getAssemblyExports, setModuleImports, BINDING, INTERNAL }) { + async init({ getAssemblyExports, setModuleImports, INTERNAL }) { const exports = await getAssemblyExports("Wasm.Browser.Bench.Sample.dll"); // Capture these two internal APIs for use at the end of the benchmark run _jiterpreter_dump_stats = INTERNAL.jiterpreter_dump_stats.bind(INTERNAL); @@ -71,17 +57,13 @@ class MainApp { setExclusions = exports.Sample.Test.SetExclusions; getFullJsonResults = exports.Sample.Test.GetFullJsonResults; - legacyExportTargetInt = BINDING.bind_static_method("[Wasm.Browser.Bench.Sample]Sample.ImportsExportsHelper:LegacyExportTargetInt"); jsExportTargetInt = exports.Sample.ImportsExportsHelper.JSExportTargetInt; - legacyExportTargetString = BINDING.bind_static_method("[Wasm.Browser.Bench.Sample]Sample.ImportsExportsHelper:LegacyExportTargetString"); jsExportTargetString = exports.Sample.ImportsExportsHelper.JSExportTargetString; setModuleImports("main.js", { Sample: { Test: { - runLegacyExportInt, runJSExportInt, - runLegacyExportString, runJSExportString, importTargetInt, importTargetString, @@ -209,9 +191,9 @@ class MainApp { return; console.error(`waitFor ${eventName} timed out`); promiseResolve(); - // Make sure this timeout is big enough that it won't cause measurements - // to be truncated! i.e. "Blazor Reach managed cold" is nearly 10k in some - // configurations right now + // Make sure this timeout is big enough that it won't cause measurements + // to be truncated! i.e. "Blazor Reach managed cold" is nearly 10k in some + // configurations right now }, 20000); document.body.appendChild(this._frame); diff --git a/src/mono/sample/wasm/browser-webpack/Wasm.Browser.WebPack.Sample.csproj b/src/mono/sample/wasm/browser-webpack/Wasm.Browser.WebPack.Sample.csproj index 8c59adb0a7b9ec..6914ae4ef6780c 100644 --- a/src/mono/sample/wasm/browser-webpack/Wasm.Browser.WebPack.Sample.csproj +++ b/src/mono/sample/wasm/browser-webpack/Wasm.Browser.WebPack.Sample.csproj @@ -15,7 +15,6 @@ $(WasmAppDir)/dotnet.native.js; $(WasmAppDir)/dotnet.native.wasm; $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts; - $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet-legacy.d.ts; $(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json;" Outputs="bin/dotnet-runtime/.npm-stamp"> @@ -23,7 +22,6 @@ - diff --git a/src/mono/sample/wasm/console-node-ts/Wasm.Console.Node.TS.Sample.csproj b/src/mono/sample/wasm/console-node-ts/Wasm.Console.Node.TS.Sample.csproj index 97375d88fd50f2..ed001522008a16 100644 --- a/src/mono/sample/wasm/console-node-ts/Wasm.Console.Node.TS.Sample.csproj +++ b/src/mono/sample/wasm/console-node-ts/Wasm.Console.Node.TS.Sample.csproj @@ -9,11 +9,9 @@ --> - diff --git a/src/mono/sample/wasm/node-webpack/Wasm.Node.WebPack.Sample.csproj b/src/mono/sample/wasm/node-webpack/Wasm.Node.WebPack.Sample.csproj index 88216d3e1258b8..175391e3457916 100644 --- a/src/mono/sample/wasm/node-webpack/Wasm.Node.WebPack.Sample.csproj +++ b/src/mono/sample/wasm/node-webpack/Wasm.Node.WebPack.Sample.csproj @@ -9,7 +9,6 @@ $(WasmAppDir)/_framework/dotnet.native.js; $(WasmAppDir)/_framework/dotnet.native.wasm; $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet.d.ts; - $(MicrosoftNetCoreAppRuntimePackNativeDir)/dotnet-legacy.d.ts; $(MicrosoftNetCoreAppRuntimePackNativeDir)/package.json;" Outputs="bin/dotnet-runtime/.npm-stamp"> @@ -17,7 +16,6 @@ - diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs index 643b2474847940..fba17daea55d99 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/WorkloadRequiredTests.cs @@ -22,7 +22,6 @@ public class WorkloadRequiredTests : BlazorWasmTestBase public static (string propertyName, bool triggerValue)[] PropertiesWithTriggerValues = new[] { ("RunAOTCompilation", true), - ("WasmEnableLegacyJsInterop", false), ("WasmEnableSIMD", false), ("WasmEnableExceptionHandling", false), ("InvariantTimezone", true), diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmNativeDefaultsTests.cs b/src/mono/wasm/Wasm.Build.Tests/WasmNativeDefaultsTests.cs index 7974e22506da33..0fd78da40da588 100644 --- a/src/mono/wasm/Wasm.Build.Tests/WasmNativeDefaultsTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/WasmNativeDefaultsTests.cs @@ -23,7 +23,6 @@ public static TheoryData SettingDifferentFromV { List<(string propertyName, bool defaultValueInRuntimePack)> defaults = new() { - ("WasmEnableLegacyJsInterop", true), ("WasmEnableSIMD", true), ("WasmEnableExceptionHandling", true), ("InvariantTimezone", false), @@ -128,10 +127,10 @@ public void DefaultsWithPublish(string config, string extraProperties, bool aot, public static TheoryData SetWasmNativeStripExplicitlyWithWasmBuildNativeTestData() => new() { - { "Debug", "falsefalse", true, false }, - { "Release", "falsefalse", true, false }, - { "Debug", "truefalse", true, true }, - { "Release", "truefalse", true, true } + { "Debug", "falsetrue", true, false }, + { "Release", "falsetrue", true, false }, + { "Debug", "truetrue", true, true }, + { "Release", "truetrue", true, true } }; [Theory] diff --git a/src/mono/wasm/build/README.md b/src/mono/wasm/build/README.md index 33b0a143946562..473ecf376bb50d 100644 --- a/src/mono/wasm/build/README.md +++ b/src/mono/wasm/build/README.md @@ -32,8 +32,6 @@ Implementation: - *after* any of the wasm build targets, use `AfterTargets="WasmBuildApp"` on that target - Avoid depending on this target, because it is available only when the workload is installed. Use `$(WasmNativeWorkload)` to check if it is installed. -- When `Module.disableDotnet6Compatibility` is set it would not pollute global namespace. - ## `Publish` Implementation: diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index ba7cf7f700ac31..3f3a97fff9b3fd 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -68,7 +68,6 @@ - $(RunAOTCompilationAfterBuild) - Run AOT compilation even after Build. By default, it is run only for publish. Defaults to false. - $(WasmAotProfilePath) - Path to an AOT profile file. - - $(WasmEnableLegacyJsInterop) - Include support for legacy JS interop. Defaults to true. - $(WasmEnableExceptionHandling) - Enable support for the WASM post MVP Exception Handling runtime extension. - $(WasmEnableSIMD) - Enable support for the WASM post MVP SIMD runtime extension. - $(WasmEnableWebcil) - Enable conversion of assembly .dlls to Webcil wrapped in .wasm (default: true) diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index a8cd689ae0c32f..8ba47325c39e6b 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -220,51 +220,29 @@ let mono_exit = (code, reason) => { }; const App = { - /** Runs a particular test in legacy interop tests - * @type {(method_name: string, args: any[]=, signature: any=) => return number} - */ - call_test_method: function (method_name, args, signature) { - // note: arguments here is the array of arguments passsed to this function - if ((arguments.length > 2) && (typeof (signature) !== "string")) - throw new Error("Invalid number of arguments for call_test_method"); - - const fqn = "[System.Runtime.InteropServices.JavaScript.Legacy.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:" + method_name; - try { - const method = App.runtime.BINDING.bind_static_method(fqn, signature); - return method.apply(null, args || []); - } catch (exc) { - console.error("exception thrown in", fqn); - throw exc; - } - }, - create_function(...args) { const code = args.pop(); const arg_count = args.length; - args.push("MONO"); - args.push("BINDING"); args.push("INTERNAL"); const userFunction = new Function(...args, code); return function (...args) { - args[arg_count + 0] = globalThis.App.runtime.MONO; - args[arg_count + 1] = globalThis.App.runtime.BINDING; - args[arg_count + 2] = globalThis.App.runtime.INTERNAL; + args[arg_count] = globalThis.App.runtime.INTERNAL; return userFunction(...args); }; }, invoke_js(js_code) { - const closedEval = function (Module, MONO, BINDING, INTERNAL, code) { + const closedEval = function (Module, INTERNAL, code) { return eval(code); }; - const res = closedEval(globalThis.App.runtime.Module, globalThis.App.runtime.MONO, globalThis.App.runtime.BINDING, globalThis.App.runtime.INTERNAL, js_code); + const res = closedEval(globalThis.App.runtime.Module, globalThis.App.runtime.INTERNAL, js_code); return (res === undefined || res === null || typeof res === "string") ? null : res.toString(); } }; -globalThis.App = App; // Necessary as System.Runtime.InteropServices.JavaScript.Tests.MarshalTests (among others) call the App.call_test_method directly +globalThis.App = App; // Necessary as tests use it function configureRuntime(dotnet, runArgs) { dotnet From 5bb58408e4a8d21eec492fc9c47cc952bac55c8a Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:29:43 +0200 Subject: [PATCH 125/189] Update HP libunwind to v1.8.0 (#96969) * Update libunwind to v1.8.0 * Add HAVE_PIPE2 definition * Revert "Rework register load in aarch64_local_resume()" https://github.com/libunwind/libunwind/commit/08bc8c0da934fefec90bc8b96dcd582054d28934 * Update Gos-linux.c * Update libunwind-version.txt --- src/native/external/libunwind-version.txt | 12 +- src/native/external/libunwind.cmake | 8 + .../libunwind/.github/workflows/CI-unix.yml | 126 +- .../libunwind/.github/workflows/CI-win.yml | 5 +- .../.github/workflows/codeql-analysis.yml | 41 + .../.github/workflows/groom-issues.yml | 25 + src/native/external/libunwind/.gitignore | 6 + src/native/external/libunwind/CMakeLists.txt | 2 +- src/native/external/libunwind/Makefile.am | 9 +- src/native/external/libunwind/README | 13 +- src/native/external/libunwind/SECURITY.md | 7 + src/native/external/libunwind/configure.ac | 399 ++-- src/native/external/libunwind/doc/Makefile.am | 18 +- .../external/libunwind/doc/_U_dyn_cancel.man | 14 +- .../external/libunwind/doc/_U_dyn_cancel.tex | 9 +- .../libunwind/doc/_U_dyn_register.man | 20 +- .../libunwind/doc/_U_dyn_register.tex | 13 +- .../libunwind/doc/libunwind-coredump.man | 211 ++ .../libunwind/doc/libunwind-coredump.tex | 138 ++ .../libunwind/doc/libunwind-dynamic.man | 585 +++--- .../libunwind/doc/libunwind-dynamic.tex | 22 +- .../external/libunwind/doc/libunwind-ia64.man | 8 +- .../external/libunwind/doc/libunwind-ia64.tex | 4 +- .../external/libunwind/doc/libunwind-nto.man | 256 +++ .../external/libunwind/doc/libunwind-nto.tex | 183 ++ .../libunwind/doc/libunwind-ptrace.man | 259 ++- .../libunwind/doc/libunwind-ptrace.tex | 209 +- .../libunwind/doc/libunwind-setjmp.man | 18 +- .../libunwind/doc/libunwind-setjmp.tex | 11 +- .../external/libunwind/doc/libunwind.man | 98 +- .../external/libunwind/doc/libunwind.tex | 94 +- .../libunwind/doc/unw_apply_reg_state.man | 20 +- .../libunwind/doc/unw_apply_reg_state.tex | 16 +- .../external/libunwind/doc/unw_backtrace.man | 8 +- .../external/libunwind/doc/unw_backtrace.tex | 10 +- .../libunwind/doc/unw_create_addr_space.man | 168 +- .../libunwind/doc/unw_create_addr_space.tex | 164 +- .../libunwind/doc/unw_destroy_addr_space.man | 10 +- .../libunwind/doc/unw_destroy_addr_space.tex | 6 +- .../libunwind/doc/unw_flush_cache.man | 24 +- .../libunwind/doc/unw_flush_cache.tex | 18 +- .../libunwind/doc/unw_get_accessors.man | 22 +- .../libunwind/doc/unw_get_accessors.tex | 18 +- .../libunwind/doc/unw_get_elf_filename.man | 110 + .../libunwind/doc/unw_get_elf_filename.tex | 71 + .../doc/unw_get_elf_filename_by_ip.man | 120 ++ .../doc/unw_get_elf_filename_by_ip.tex | 78 + .../external/libunwind/doc/unw_get_fpreg.man | 30 +- .../external/libunwind/doc/unw_get_fpreg.tex | 26 +- .../libunwind/doc/unw_get_proc_info.man | 58 +- .../libunwind/doc/unw_get_proc_info.tex | 54 +- .../libunwind/doc/unw_get_proc_info_by_ip.man | 34 +- .../libunwind/doc/unw_get_proc_info_by_ip.tex | 30 +- .../doc/unw_get_proc_info_in_range.man | 26 +- .../doc/unw_get_proc_info_in_range.tex | 26 +- .../libunwind/doc/unw_get_proc_name.man | 26 +- .../libunwind/doc/unw_get_proc_name.tex | 22 +- .../libunwind/doc/unw_get_proc_name_by_ip.man | 104 +- .../libunwind/doc/unw_get_proc_name_by_ip.tex | 32 +- .../external/libunwind/doc/unw_get_reg.man | 32 +- .../external/libunwind/doc/unw_get_reg.tex | 28 +- .../external/libunwind/doc/unw_getcontext.man | 16 +- .../external/libunwind/doc/unw_getcontext.tex | 12 +- .../external/libunwind/doc/unw_init_local.man | 22 +- .../external/libunwind/doc/unw_init_local.tex | 17 +- .../libunwind/doc/unw_init_remote.man | 24 +- .../libunwind/doc/unw_init_remote.tex | 19 +- .../external/libunwind/doc/unw_is_fpreg.man | 18 +- .../external/libunwind/doc/unw_is_fpreg.tex | 14 +- .../libunwind/doc/unw_is_signal_frame.man | 35 +- .../libunwind/doc/unw_is_signal_frame.tex | 31 +- .../libunwind/doc/unw_reg_states_iterate.man | 18 +- .../libunwind/doc/unw_reg_states_iterate.tex | 14 +- .../external/libunwind/doc/unw_regname.man | 10 +- .../external/libunwind/doc/unw_regname.tex | 6 +- .../external/libunwind/doc/unw_resume.man | 14 +- .../external/libunwind/doc/unw_resume.tex | 10 +- .../libunwind/doc/unw_set_cache_size.man | 14 +- .../libunwind/doc/unw_set_cache_size.tex | 10 +- .../libunwind/doc/unw_set_caching_policy.man | 16 +- .../libunwind/doc/unw_set_caching_policy.tex | 12 +- .../external/libunwind/doc/unw_set_fpreg.man | 32 +- .../external/libunwind/doc/unw_set_fpreg.tex | 28 +- .../doc/unw_set_iterate_phdr_function.man | 83 + .../doc/unw_set_iterate_phdr_function.tex | 57 + .../external/libunwind/doc/unw_set_reg.man | 32 +- .../external/libunwind/doc/unw_set_reg.tex | 28 +- .../external/libunwind/doc/unw_step.man | 12 +- .../external/libunwind/doc/unw_step.tex | 8 +- .../external/libunwind/doc/unw_strerror.man | 12 +- .../external/libunwind/doc/unw_strerror.tex | 2 +- .../external/libunwind/include/compiler.h | 10 + src/native/external/libunwind/include/dwarf.h | 4 +- .../external/libunwind/include/dwarf_i.h | 32 +- .../libunwind/include/libunwind-aarch64.h | 13 +- .../libunwind/include/libunwind-arm.h | 51 +- .../libunwind/include/libunwind-common.h.in | 23 + .../libunwind/include/libunwind-coredump.h | 2 + .../libunwind/include/libunwind-hppa.h | 9 + .../libunwind/include/libunwind-ia64.h | 8 + .../libunwind/include/libunwind-loongarch64.h | 11 +- .../libunwind/include/libunwind-mips.h | 9 + .../libunwind/include/libunwind-nto.h | 69 + .../libunwind/include/libunwind-ppc32.h | 9 + .../libunwind/include/libunwind-ppc64.h | 9 + .../libunwind/include/libunwind-ptrace.h | 2 + .../libunwind/include/libunwind-riscv.h | 11 +- .../libunwind/include/libunwind-s390x.h | 10 +- .../external/libunwind/include/libunwind-sh.h | 9 + .../libunwind/include/libunwind-tilegx.h | 161 -- .../libunwind/include/libunwind-x86.h | 3 + .../libunwind/include/libunwind-x86_64.h | 11 +- .../external/libunwind/include/libunwind.h.in | 2 - .../external/libunwind/include/libunwind_i.h | 114 +- .../external/libunwind/include/remote.h | 16 +- .../libunwind/include/remote/win/unistd.h | 6 + .../libunwind/include/tdep-aarch64/jmpbuf.h | 8 + .../include/tdep-aarch64/libunwind_i.h | 15 +- .../libunwind/include/tdep-arm/libunwind_i.h | 3 + .../libunwind/include/tdep-hppa/libunwind_i.h | 3 + .../libunwind/include/tdep-ia64/libunwind_i.h | 3 + .../include/tdep-loongarch64/libunwind_i.h | 5 +- .../libunwind/include/tdep-mips/libunwind_i.h | 3 + .../include/tdep-ppc32/libunwind_i.h | 3 + .../include/tdep-ppc64/libunwind_i.h | 3 + .../include/tdep-riscv/libunwind_i.h | 7 +- .../include/tdep-s390x/libunwind_i.h | 5 +- .../libunwind/include/tdep-sh/libunwind_i.h | 3 + .../include/tdep-tilegx/dwarf-config.h | 50 - .../libunwind/include/tdep-tilegx/jmpbuf.h | 33 - .../include/tdep-tilegx/libunwind_i.h | 260 --- .../libunwind/include/tdep-x86/jmpbuf.h | 1 + .../libunwind/include/tdep-x86/libunwind_i.h | 3 + .../libunwind/include/tdep-x86_64/jmpbuf.h | 5 + .../include/tdep-x86_64/libunwind_i.h | 11 +- .../libunwind/include/tdep/dwarf-config.h | 2 - .../external/libunwind/include/tdep/jmpbuf.h | 2 - .../libunwind/include/tdep/libunwind_i.h.in | 2 - .../external/libunwind/src/CMakeLists.txt | 8 + src/native/external/libunwind/src/Makefile.am | 1776 ++++++++++------- .../external/libunwind/src/aarch64/Gglobal.c | 2 - .../external/libunwind/src/aarch64/Ginit.c | 274 +-- .../libunwind/src/aarch64/Ginit_local.c | 2 +- .../libunwind/src/aarch64/Gis_signal_frame.c | 62 +- .../Ginit_local.c => aarch64/Gos-freebsd.c} | 78 +- .../libunwind/src/aarch64/Gos-linux.c | 145 ++ .../{tilegx/is_fpreg.c => aarch64/Gos-qnx.c} | 14 +- .../external/libunwind/src/aarch64/Gresume.c | 145 +- .../external/libunwind/src/aarch64/Gstep.c | 617 +++++- .../external/libunwind/src/aarch64/Gtrace.c | 35 +- .../Linit_local.c => aarch64/Los-freebsd.c} | 2 +- .../{tilegx/Lglobal.c => aarch64/Los-linux.c} | 2 +- .../src/{tilegx/Linit.c => aarch64/Los-qnx.c} | 2 +- .../libunwind/src/aarch64/gen-offsets.c | 68 - .../libunwind/src/aarch64/getcontext.S | 8 +- .../x86/jmpbuf.h => src/aarch64/longjmp.S} | 29 +- .../external/libunwind/src/aarch64/offsets.h | 56 - .../libunwind/src/aarch64/setcontext.S | 60 + .../libunwind/src/aarch64/siglongjmp.S | 61 +- .../libunwind/src/aarch64/ucontext_i.h | 74 + .../external/libunwind/src/aarch64/unwind_i.h | 4 +- .../external/libunwind/src/arm/Gex_tables.c | 2 +- src/native/external/libunwind/src/arm/Ginit.c | 76 +- src/native/external/libunwind/src/arm/Gstep.c | 19 +- .../external/libunwind/src/arm/Gtrace.c | 10 +- .../libunwind/src/coredump/_UCD_access_mem.c | 2 +- .../src/coredump/_UCD_access_reg_linux.c | 11 +- .../src/coredump/_UCD_access_reg_qnx.c | 158 ++ .../libunwind/src/coredump/_UCD_accessors.c | 3 +- .../libunwind/src/coredump/_UCD_create.c | 6 +- .../src/coredump/_UCD_elf_map_image.c | 8 +- .../src/coredump/_UCD_get_elf_filename.c | 122 ++ .../src/coredump/_UCD_get_mapinfo_linux.c | 14 +- .../src/coredump/_UCD_get_mapinfo_qnx.c | 159 ++ .../src/coredump/_UCD_get_proc_name.c | 23 +- .../coredump/_UCD_get_threadinfo_prstatus.c | 77 +- .../libunwind/src/coredump/_UCD_internal.h | 6 +- .../src/coredump/_UPT_access_fpreg.c | 7 +- .../coredump/_UPT_get_dyn_info_list_addr.c | 6 +- .../src/coredump/_UPT_put_unwind_info.c | 4 +- .../libunwind/src/coredump/_UPT_resume.c | 4 +- .../libunwind/src/coredump/ucd_file_table.c | 6 +- .../external/libunwind/src/dl-iterate-phdr.c | 9 +- .../external/libunwind/src/dwarf/Gexpr.c | 2 +- .../libunwind/src/dwarf/Gfind_proc_info-lsb.c | 24 +- .../libunwind/src/dwarf/Gfind_unwind_table.c | 14 +- .../src/dwarf/Gget_proc_info_in_range.c | 22 +- .../external/libunwind/src/dwarf/Gparser.c | 27 +- src/native/external/libunwind/src/elfxx.c | 573 +++++- src/native/external/libunwind/src/elfxx.h | 21 +- .../external/libunwind/src/hppa/Ginit.c | 22 +- .../external/libunwind/src/ia64/Ginit.c | 20 +- .../external/libunwind/src/ia64/Gtables.c | 4 +- .../libunwind/src/loongarch64/Gglobal.c | 2 - .../libunwind/src/loongarch64/Ginit.c | 242 +-- .../libunwind/src/mi/Gaddress_validator.c | 302 +++ .../libunwind/src/mi/Gdestroy_addr_space.c | 2 +- .../external/libunwind/src/mi/Gdyn-remote.c | 5 +- .../src/mi/Gfind_dynamic_proc_info.c | 7 +- .../libunwind/src/mi/Gget_elf_filename.c | 77 + .../src/mi/Gset_iterate_phdr_function.c | 23 + .../Laddress_validator.c} | 2 +- .../Lget_elf_filename.c} | 2 +- .../Lset_iterate_phdr_function.c} | 2 +- .../external/libunwind/src/mi/flush_cache.c | 8 +- .../external/libunwind/src/mi/mempool.c | 10 +- .../external/libunwind/src/mips/Ginit.c | 15 + .../libunwind/src/nto/unw_nto_access_fpreg.c | 43 + .../libunwind/src/nto/unw_nto_access_mem.c | 79 + .../libunwind/src/nto/unw_nto_access_reg.c | 202 ++ .../libunwind/src/nto/unw_nto_accessors.c | 44 + .../libunwind/src/nto/unw_nto_create.c | 102 + .../libunwind/src/nto/unw_nto_destroy.c | 81 + .../external/libunwind/src/nto/unw_nto_elf.c | 33 + .../src/nto/unw_nto_find_proc_info.c | 100 + .../src/nto/unw_nto_get_dyn_info_list_addr.c | 38 + .../src/nto/unw_nto_get_elf_filename.c | 51 + .../libunwind/src/nto/unw_nto_get_proc_name.c | 88 + .../libunwind/src/nto/unw_nto_internal.h | 44 + .../src/nto/unw_nto_put_unwind_info.c | 37 + .../libunwind/src/nto/unw_nto_resume.c | 39 + .../external/libunwind/src/os-freebsd.c | 10 +- src/native/external/libunwind/src/os-hpux.c | 7 +- src/native/external/libunwind/src/os-linux.c | 57 +- src/native/external/libunwind/src/os-linux.h | 11 +- src/native/external/libunwind/src/os-qnx.c | 334 +++- src/native/external/libunwind/src/os-qnx.h | 118 ++ .../external/libunwind/src/os-solaris.c | 11 +- .../external/libunwind/src/ppc32/Ginit.c | 28 +- .../external/libunwind/src/ppc32/Gstep.c | 3 + .../external/libunwind/src/ppc32/ucontext_i.h | 4 +- .../external/libunwind/src/ppc64/Ginit.c | 28 +- .../external/libunwind/src/ppc64/Gstep.c | 249 ++- .../libunwind/src/ptrace/_UPT_access_fpreg.c | 12 +- .../libunwind/src/ptrace/_UPT_access_mem.c | 2 +- .../libunwind/src/ptrace/_UPT_access_reg.c | 50 +- .../libunwind/src/ptrace/_UPT_accessors.c | 3 +- .../libunwind/src/ptrace/_UPT_create.c | 2 + .../src/ptrace/_UPT_get_dyn_info_list_addr.c | 6 +- .../src/ptrace/_UPT_get_elf_filename.c | 38 + .../src/ptrace/_UPT_put_unwind_info.c | 2 +- .../libunwind/src/ptrace/_UPT_reg_offset.c | 58 - .../libunwind/src/ptrace/_UPT_resume.c | 4 +- .../src/remote/mac/missing-functions.c | 6 + .../external/libunwind/src/riscv/Gglobal.c | 2 - .../external/libunwind/src/riscv/Ginit.c | 247 +-- .../external/libunwind/src/s390x/Gglobal.c | 2 - .../external/libunwind/src/s390x/Ginit.c | 176 +- .../external/libunwind/src/setjmp/longjmp.c | 10 +- .../external/libunwind/src/setjmp/setjmp_i.h | 13 +- .../libunwind/src/setjmp/siglongjmp.c | 16 +- src/native/external/libunwind/src/sh/Ginit.c | 22 +- .../libunwind/src/tilegx/Gapply_reg_state.c | 37 - .../libunwind/src/tilegx/Gcreate_addr_space.c | 62 - .../libunwind/src/tilegx/Gget_proc_info.c | 48 - .../libunwind/src/tilegx/Gget_save_loc.c | 62 - .../external/libunwind/src/tilegx/Gglobal.c | 64 - .../external/libunwind/src/tilegx/Ginit.c | 166 -- .../libunwind/src/tilegx/Ginit_remote.c | 47 - .../libunwind/src/tilegx/Gis_signal_frame.c | 118 -- .../src/tilegx/Greg_states_iterate.c | 37 - .../external/libunwind/src/tilegx/Gregs.c | 76 - .../external/libunwind/src/tilegx/Gresume.c | 94 - .../external/libunwind/src/tilegx/Gstep.c | 53 - .../libunwind/src/tilegx/Lget_save_loc.c | 5 - .../libunwind/src/tilegx/Linit_remote.c | 5 - .../libunwind/src/tilegx/Lis_signal_frame.c | 5 - .../src/tilegx/Lreg_states_iterate.c | 5 - .../external/libunwind/src/tilegx/Lregs.c | 5 - .../external/libunwind/src/tilegx/Lresume.c | 5 - .../external/libunwind/src/tilegx/Lstep.c | 5 - .../external/libunwind/src/tilegx/elfxx.c | 27 - .../libunwind/src/tilegx/gen-offsets.c | 30 - .../libunwind/src/tilegx/getcontext.S | 36 - .../external/libunwind/src/tilegx/init.h | 63 - .../external/libunwind/src/tilegx/offsets.h | 12 - .../external/libunwind/src/tilegx/regname.c | 55 - .../libunwind/src/tilegx/siglongjmp.S | 7 - .../external/libunwind/src/tilegx/unwind_i.h | 46 - src/native/external/libunwind/src/x86/Ginit.c | 76 +- .../external/libunwind/src/x86/Gos-freebsd.c | 3 +- .../external/libunwind/src/x86/Gos-linux.c | 46 +- src/native/external/libunwind/src/x86/Gstep.c | 71 +- .../external/libunwind/src/x86/unwind_i.h | 2 - .../libunwind/src/x86_64/Gcreate_addr_space.c | 8 +- .../external/libunwind/src/x86_64/Gglobal.c | 2 - .../external/libunwind/src/x86_64/Ginit.c | 266 +-- .../libunwind/src/x86_64/Ginit_remote.c | 2 +- .../libunwind/src/x86_64/Gos-freebsd.c | 86 +- .../external/libunwind/src/x86_64/Gos-linux.c | 38 +- .../external/libunwind/src/x86_64/Gos-qnx.c | 214 ++ .../libunwind/src/x86_64/Gos-solaris.c | 6 + .../external/libunwind/src/x86_64/Gregs.c | 4 +- .../external/libunwind/src/x86_64/Gresume.c | 3 +- .../external/libunwind/src/x86_64/Gstep.c | 49 +- .../external/libunwind/src/x86_64/Gtrace.c | 7 +- .../external/libunwind/src/x86_64/Los-qnx.c | 29 + .../libunwind/src/x86_64/getcontext.S | 1 + .../external/libunwind/src/x86_64/is_fpreg.c | 2 +- .../external/libunwind/src/x86_64/offsets.h | 3 - .../libunwind/src/x86_64/setcontext.S | 1 + .../libunwind/src/x86_64/siglongjmp.S | 18 + .../libunwind/src/x86_64/ucontext_i.h | 19 + .../external/libunwind/src/x86_64/unwind_i.h | 2 + .../external/libunwind/tests/Gperf-simple.c | 22 +- .../external/libunwind/tests/Gperf-trace.c | 26 +- .../external/libunwind/tests/Gtest-bt.c | 50 +- .../libunwind/tests/Gtest-concurrent.c | 4 +- .../external/libunwind/tests/Gtest-exc.c | 8 +- .../external/libunwind/tests/Gtest-init.cxx | 16 +- .../libunwind/tests/Gtest-resume-sig.c | 9 +- .../external/libunwind/tests/Gtest-trace.c | 44 +- .../tests/Gx64-test-dwarf-expressions.c | 7 +- .../external/libunwind/tests/Lrs-race.c | 260 +-- .../libunwind/tests/Ltest-init-local-signal.c | 16 +- .../libunwind/tests/Ltest-mem-validate.c | 63 +- .../external/libunwind/tests/Ltest-nocalloc.c | 8 +- .../external/libunwind/tests/Makefile.am | 68 +- .../tests/aarch64-test-frame-record.c | 390 ++++ .../libunwind/tests/aarch64-test-plt.c | 175 ++ .../libunwind/tests/check-namespace.sh.in | 83 +- .../external/libunwind/tests/flush-cache.S | 16 - src/native/external/libunwind/tests/ident.c | 24 + src/native/external/libunwind/tests/ident.h | 28 + src/native/external/libunwind/tests/mapper.c | 11 +- .../external/libunwind/tests/ppc64-test-plt.c | 164 ++ .../libunwind/tests/run-coredump-unwind | 2 +- .../external/libunwind/tests/test-async-sig.c | 12 +- .../libunwind/tests/test-coredump-unwind.c | 27 +- .../libunwind/tests/test-init-remote.c | 23 +- .../external/libunwind/tests/test-mem.c | 10 +- .../external/libunwind/tests/test-proc-info.c | 4 +- .../libunwind/tests/test-ptrace-misc.c | 3 +- .../external/libunwind/tests/test-ptrace.c | 49 +- .../external/libunwind/tests/test-reg-state.c | 8 +- .../external/libunwind/tests/test-setjmp.c | 136 +- .../tests/x64-test-dwarf-expressions.S | 3 + .../tests/x64-unwind-badjmp-signal-frame.c | 11 +- .../external/libunwind_extras/CMakeLists.txt | 1 + .../external/libunwind_extras/config.h.in | 2 + .../external/libunwind_extras/configure.cmake | 3 + 341 files changed, 11308 insertions(+), 6681 deletions(-) create mode 100644 src/native/external/libunwind/.github/workflows/codeql-analysis.yml create mode 100644 src/native/external/libunwind/.github/workflows/groom-issues.yml create mode 100644 src/native/external/libunwind/SECURITY.md create mode 100644 src/native/external/libunwind/doc/libunwind-coredump.man create mode 100644 src/native/external/libunwind/doc/libunwind-coredump.tex create mode 100644 src/native/external/libunwind/doc/libunwind-nto.man create mode 100644 src/native/external/libunwind/doc/libunwind-nto.tex create mode 100644 src/native/external/libunwind/doc/unw_get_elf_filename.man create mode 100644 src/native/external/libunwind/doc/unw_get_elf_filename.tex create mode 100644 src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.man create mode 100644 src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.tex create mode 100644 src/native/external/libunwind/doc/unw_set_iterate_phdr_function.man create mode 100644 src/native/external/libunwind/doc/unw_set_iterate_phdr_function.tex create mode 100644 src/native/external/libunwind/include/libunwind-nto.h delete mode 100644 src/native/external/libunwind/include/libunwind-tilegx.h delete mode 100644 src/native/external/libunwind/include/tdep-tilegx/dwarf-config.h delete mode 100644 src/native/external/libunwind/include/tdep-tilegx/jmpbuf.h delete mode 100644 src/native/external/libunwind/include/tdep-tilegx/libunwind_i.h rename src/native/external/libunwind/src/{tilegx/Ginit_local.c => aarch64/Gos-freebsd.c} (50%) create mode 100644 src/native/external/libunwind/src/aarch64/Gos-linux.c rename src/native/external/libunwind/src/{tilegx/is_fpreg.c => aarch64/Gos-qnx.c} (83%) rename src/native/external/libunwind/src/{tilegx/Linit_local.c => aarch64/Los-freebsd.c} (81%) rename src/native/external/libunwind/src/{tilegx/Lglobal.c => aarch64/Los-linux.c} (82%) rename src/native/external/libunwind/src/{tilegx/Linit.c => aarch64/Los-qnx.c} (83%) delete mode 100644 src/native/external/libunwind/src/aarch64/gen-offsets.c rename src/native/external/libunwind/{include/x86/jmpbuf.h => src/aarch64/longjmp.S} (69%) delete mode 100644 src/native/external/libunwind/src/aarch64/offsets.h create mode 100644 src/native/external/libunwind/src/aarch64/setcontext.S create mode 100644 src/native/external/libunwind/src/aarch64/ucontext_i.h create mode 100644 src/native/external/libunwind/src/coredump/_UCD_access_reg_qnx.c create mode 100644 src/native/external/libunwind/src/coredump/_UCD_get_elf_filename.c create mode 100644 src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_qnx.c create mode 100644 src/native/external/libunwind/src/mi/Gaddress_validator.c create mode 100644 src/native/external/libunwind/src/mi/Gget_elf_filename.c create mode 100644 src/native/external/libunwind/src/mi/Gset_iterate_phdr_function.c rename src/native/external/libunwind/src/{tilegx/Lcreate_addr_space.c => mi/Laddress_validator.c} (77%) rename src/native/external/libunwind/src/{tilegx/Lapply_reg_state.c => mi/Lget_elf_filename.c} (78%) rename src/native/external/libunwind/src/{tilegx/Lget_proc_info.c => mi/Lset_iterate_phdr_function.c} (73%) create mode 100644 src/native/external/libunwind/src/nto/unw_nto_access_fpreg.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_access_mem.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_access_reg.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_accessors.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_create.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_destroy.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_elf.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_find_proc_info.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_get_dyn_info_list_addr.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_get_elf_filename.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_get_proc_name.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_internal.h create mode 100644 src/native/external/libunwind/src/nto/unw_nto_put_unwind_info.c create mode 100644 src/native/external/libunwind/src/nto/unw_nto_resume.c create mode 100644 src/native/external/libunwind/src/os-qnx.h create mode 100644 src/native/external/libunwind/src/ptrace/_UPT_get_elf_filename.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gapply_reg_state.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gcreate_addr_space.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gget_proc_info.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gget_save_loc.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gglobal.c delete mode 100644 src/native/external/libunwind/src/tilegx/Ginit.c delete mode 100644 src/native/external/libunwind/src/tilegx/Ginit_remote.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gis_signal_frame.c delete mode 100644 src/native/external/libunwind/src/tilegx/Greg_states_iterate.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gregs.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gresume.c delete mode 100644 src/native/external/libunwind/src/tilegx/Gstep.c delete mode 100644 src/native/external/libunwind/src/tilegx/Lget_save_loc.c delete mode 100644 src/native/external/libunwind/src/tilegx/Linit_remote.c delete mode 100644 src/native/external/libunwind/src/tilegx/Lis_signal_frame.c delete mode 100644 src/native/external/libunwind/src/tilegx/Lreg_states_iterate.c delete mode 100644 src/native/external/libunwind/src/tilegx/Lregs.c delete mode 100644 src/native/external/libunwind/src/tilegx/Lresume.c delete mode 100644 src/native/external/libunwind/src/tilegx/Lstep.c delete mode 100644 src/native/external/libunwind/src/tilegx/elfxx.c delete mode 100644 src/native/external/libunwind/src/tilegx/gen-offsets.c delete mode 100644 src/native/external/libunwind/src/tilegx/getcontext.S delete mode 100644 src/native/external/libunwind/src/tilegx/init.h delete mode 100644 src/native/external/libunwind/src/tilegx/offsets.h delete mode 100644 src/native/external/libunwind/src/tilegx/regname.c delete mode 100644 src/native/external/libunwind/src/tilegx/siglongjmp.S delete mode 100644 src/native/external/libunwind/src/tilegx/unwind_i.h create mode 100644 src/native/external/libunwind/src/x86_64/Gos-qnx.c create mode 100644 src/native/external/libunwind/src/x86_64/Los-qnx.c delete mode 100644 src/native/external/libunwind/src/x86_64/offsets.h create mode 100644 src/native/external/libunwind/tests/aarch64-test-frame-record.c create mode 100644 src/native/external/libunwind/tests/aarch64-test-plt.c create mode 100644 src/native/external/libunwind/tests/ident.h create mode 100644 src/native/external/libunwind/tests/ppc64-test-plt.c diff --git a/src/native/external/libunwind-version.txt b/src/native/external/libunwind-version.txt index de52659948a1cb..f7064e3c6e1fe7 100644 --- a/src/native/external/libunwind-version.txt +++ b/src/native/external/libunwind-version.txt @@ -1,6 +1,8 @@ -v1.7.0 -https://github.com/libunwind/libunwind/commit/688caaf6ef9853cc26ad8bd1706804d48a0df0bc +v1.8.0 +https://github.com/libunwind/libunwind/commit/2c2566c79c4f9cb505e6a23e4ab884d856ef88fe -Apply https://github.com/libunwind/libunwind/pull/457 -Apply https://github.com/libunwind/libunwind/pull/455 -Apply https://github.com/libunwind/libunwind/pull/460 +Apply https://github.com/libunwind/libunwind/pull/700 +Apply https://github.com/libunwind/libunwind/pull/701 +Apply https://github.com/libunwind/libunwind/pull/703 +Apply https://github.com/libunwind/libunwind/pull/704 +Revert https://github.com/libunwind/libunwind/pull/503 # issue: https://github.com/libunwind/libunwind/issues/702 diff --git a/src/native/external/libunwind.cmake b/src/native/external/libunwind.cmake index c4456cf93b71ba..5dcca157e23ae6 100644 --- a/src/native/external/libunwind.cmake +++ b/src/native/external/libunwind.cmake @@ -45,6 +45,7 @@ set(libunwind_la_SOURCES_generic mi/Gget_fpreg.c mi/Gset_fpreg.c mi/Gset_caching_policy.c mi/Gset_cache_size.c + mi/Gaddress_validator.c ) set(libunwind_la_SOURCES_os_linux @@ -81,6 +82,8 @@ if(CLR_CMAKE_TARGET_LINUX) set(libunwind_la_SOURCES_x86_64_os_local x86_64/Los-linux.c) set(libunwind_la_SOURCES_arm_os arm/Gos-linux.c) set(libunwind_la_SOURCES_arm_os_local arm/Los-linux.c) + set(libunwind_la_SOURCES_aarch64_os aarch64/Gos-linux.c) + set(libunwind_la_SOURCES_aarch64_os_local aarch64/Los-linux.c) list(APPEND libunwind_coredump_la_SOURCES coredump/_UCD_access_reg_linux.c) elseif(CLR_CMAKE_TARGET_FREEBSD) set(libunwind_la_SOURCES_os ${libunwind_la_SOURCES_os_freebsd}) @@ -92,6 +95,8 @@ elseif(CLR_CMAKE_TARGET_FREEBSD) set(libunwind_la_SOURCES_x86_64_os_local x86_64/Los-freebsd.c) set(libunwind_la_SOURCES_arm_os arm/Gos-freebsd.c) set(libunwind_la_SOURCES_arm_os_local arm/Los-freebsd.c) + set(libunwind_la_SOURCES_aarch64_os aarch64/Gos-freebsd.c) + set(libunwind_la_SOURCES_aarch64_os_local aarch64/Los-freebsd.c) list(APPEND libunwind_coredump_la_SOURCES coredump/_UCD_access_reg_freebsd.c) elseif(CLR_CMAKE_HOST_SUNOS) set(libunwind_la_SOURCES_os ${libunwind_la_SOURCES_os_solaris}) @@ -224,6 +229,7 @@ set(libunwind_la_SOURCES_aarch64_common # The list of files that go into libunwind: set(libunwind_la_SOURCES_aarch64 ${libunwind_la_SOURCES_aarch64_common} + ${libunwind_la_SOURCES_aarch64_os_local} ${libunwind_la_SOURCES_local} aarch64/Lapply_reg_state.c aarch64/Lreg_states_iterate.c aarch64/Lcreate_addr_space.c aarch64/Lget_proc_info.c @@ -234,8 +240,10 @@ set(libunwind_la_SOURCES_aarch64 aarch64/getcontext.S ) +# The list of files that go into libunwind-aarch64: set(libunwind_aarch64_la_SOURCES_aarch64 ${libunwind_la_SOURCES_aarch64_common} + ${libunwind_la_SOURCES_aarch64_os} ${libunwind_la_SOURCES_generic} aarch64/Gapply_reg_state.c aarch64/Greg_states_iterate.c aarch64/Gcreate_addr_space.c aarch64/Gget_proc_info.c diff --git a/src/native/external/libunwind/.github/workflows/CI-unix.yml b/src/native/external/libunwind/.github/workflows/CI-unix.yml index 7d48b48daec03c..cb237f5e4e80ee 100644 --- a/src/native/external/libunwind/.github/workflows/CI-unix.yml +++ b/src/native/external/libunwind/.github/workflows/CI-unix.yml @@ -1,5 +1,8 @@ name: CI - Unix +permissions: + contents: read + on: pull_request: paths: @@ -16,106 +19,118 @@ on: - master jobs: - build: - runs-on: ubuntu-latest - name: build-${{ join(matrix.*, ' ') }} + build-native: + runs-on: ubuntu-22.04 + name: build-${{ matrix.toolchain.compiler }}-${{ matrix.target.arch }}${{ matrix.optimization.CFLAGS }} + strategy: fail-fast: false matrix: - HOST: - - x86_64-linux-gnu - - x86-linux-gnu - - arm-linux-gnueabihf - - aarch64-linux-gnu - - mipsel-linux-gnu - - powerpc64-linux-gnu - OPT: - - O0 - - O3 + target: + - { arch: i686, triple: i686-pc-linux-gnu, CFLAGS: -m32 } + - { arch: x86_64, triple: x86_64-pc-linux-gnu, CFLAGS: } + toolchain: + - { compiler: gcc, CC: gcc-12, CXX: g++-12 } + - { compiler: clang, CC: clang-13, CXX: clang++-13 } + optimization: + - { CFLAGS: -O0 } + - { CFLAGS: -O3 } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - name: Setup + if: ${{ matrix.target.arch }} = 'i686' run: | - HOST=${{ matrix.HOST }} - if [ $HOST = 'x86-linux-gnu' ]; then - sudo apt-get update - sudo apt-get install -yqq -o=Dpkg::Use-Pty=0 g++-multilib - elif [ $HOST != 'x86_64-linux-gnu' ]; then - sudo apt-get update - sudo apt-get install -yqq -o=Dpkg::Use-Pty=0 g++-$HOST - fi + sudo apt update + sudo apt install -y g++-12-multilib + - name: Configure run: | set -x - HOST=${{ matrix.HOST }} - BUILD=x86_64-linux-gnu - if [ $HOST = 'x86-linux-gnu' ]; then - CFLAGS="-m32" - CXXFLAGS="-m32" - BUILD=x86-linux-gnu - fi - export CFLAGS="$CFLAGS -${{ matrix.OPT }}" - export CXXFLAGS="$CXXFLAGS -${{ matrix.OPT}}" autoreconf -i - ./configure --build=$BUILD --host=$HOST + ./configure --build=x86_64-pc-linux-gnu --host=${{ matrix.target.triple }} + env: + CC: ${{ matrix.toolchain.CC }} + CXX: ${{ matrix.toolchain.CXX }} + CFLAGS: "${{ matrix.target.CFLAGS }} ${{ matrix.optimization.CFLAGS }} -Wall -Wextra" + CXXFLAGS: "${{ matrix.target.CFLAGS }} ${{ matrix.optimization.CFLAGS }} -Wall -Wextra" + LDFLAGS: ${{ matrix.target.CFLAGS }} + + - name: Build + run: | make -j8 + - name: Test (native) - if: ${{ success() && (matrix.HOST == 'x86_64-linux-gnu' || matrix.HOST == 'x86-linux-gnu') }} + if: ${{ success() }} run: | set -x sudo bash -c 'echo core.%p.%p > /proc/sys/kernel/core_pattern' ulimit -c unlimited - make check -j32 + make check -j8 + - name: Show Logs if: ${{ failure() }} run: | cat tests/test-suite.log 2>/dev/null - build-cross-qemu: - runs-on: ubuntu-latest - name: build-cross-qemu-${{ matrix.config.target }} + build-cross: + runs-on: ubuntu-22.04 + name: build-cross-${{ matrix.config.target }} strategy: fail-fast: false matrix: config: - - {target: arm, toolchain: g++-arm-linux-gnueabi, host: arm-linux-gnueabi, qemu: arm } - - {target: armhf, toolchain: g++-arm-linux-gnueabihf, host: arm-linux-gnueabihf, qemu: arm } - - {target: aarch64, toolchain: g++-aarch64-linux-gnu, host: aarch64-linux-gnu, qemu: aarch64 } - - {target: riscv64, toolchain: g++-riscv64-linux-gnu, host: riscv64-linux-gnu, qemu: riscv64 } - - {target: ppc, toolchain: g++-powerpc-linux-gnu, host: powerpc-linux-gnu, qemu: ppc } - - {target: ppc64, toolchain: g++-powerpc64-linux-gnu, host: powerpc64-linux-gnu, qemu: ppc64 } - - {target: ppc64le, toolchain: g++-powerpc64le-linux-gnu, host: powerpc64le-linux-gnu, qemu: ppc64le } - - {target: s390x, toolchain: g++-s390x-linux-gnu, host: s390x-linux-gnu, qemu: s390x } - - {target: mips, toolchain: g++-mips-linux-gnu, host: mips-linux-gnu, qemu: mips } - - {target: mips64, toolchain: g++-mips64-linux-gnuabi64, host: mips64-linux-gnuabi64, qemu: mips64 } - - {target: mipsel, toolchain: g++-mipsel-linux-gnu, host: mipsel-linux-gnu, qemu: mipsel } - - {target: mips64el,toolchain: g++-mips64el-linux-gnuabi64, host: mips64el-linux-gnuabi64,qemu: mips64el } + - {target: arm, host: arm-linux-gnueabi, qemu: arm, gccver: 12 } + - {target: armhf, host: arm-linux-gnueabihf, qemu: arm, gccver: 12 } + - {target: aarch64, host: aarch64-linux-gnu, qemu: aarch64, gccver: 12 } + - {target: riscv64, host: riscv64-linux-gnu, qemu: riscv64, gccver: 12 } + - {target: ppc, host: powerpc-linux-gnu, qemu: ppc, gccver: 12 } + - {target: ppc64, host: powerpc64-linux-gnu, qemu: ppc64, gccver: 12 } + - {target: ppc64le, host: powerpc64le-linux-gnu, qemu: ppc64le, gccver: 12 } + - {target: s390x, host: s390x-linux-gnu, qemu: s390x, gccver: 12 } + - {target: mips, host: mips-linux-gnu, qemu: mips, gccver: 10 } + - {target: mips64, host: mips64-linux-gnuabi64, qemu: mips64, gccver: 10 } + - {target: mipsel, host: mipsel-linux-gnu, qemu: mipsel, gccver: 10 } + - {target: mips64el, host: mips64el-linux-gnuabi64, qemu: mips64el, gccver: 10 } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install QEMU # this ensure install latest qemu on ubuntu, apt get version is old env: QEMU_SRC: "http://archive.ubuntu.com/ubuntu/pool/universe/q/qemu" - QEMU_VER: "qemu-user-static_4\\.2-.*_amd64.deb$" + QEMU_VER: "qemu-user-static_7\\.2.*_amd64.deb$" run: | DEB=`curl -s $QEMU_SRC/ | grep -o -E 'href="([^"#]+)"' | cut -d'"' -f2 | grep $QEMU_VER | tail -1` wget $QEMU_SRC/$DEB sudo dpkg -i $DEB - - name: Install ${{ matrix.config.toolchain }} + + - name: Install ${{ matrix.config.host }} Toolchain run: | sudo apt update - sudo apt install ${{ matrix.config.toolchain }} -y + sudo apt install g++-${{ matrix.config.gccver }}-${{ matrix.config.host }} -y + - name: Configure with ${{ matrix.config.cc }} run: | set -x autoreconf -i BUILD=x86_64-linux-gnu - ./configure --build=$BUILD --host=${{ matrix.config.host }} --with-testdriver=$(pwd)/scripts/qemu-test-driver + ./configure --build=$BUILD --host=${{ matrix.config.host }} --with-testdriver=$(pwd)/scripts/qemu-test-driver --enable-debug + env: + CC: ${{ matrix.config.host }}-gcc-${{ matrix.config.gccver }} + CXX: ${{ matrix.config.host }}-g++-${{ matrix.config.gccver }} + - name: Build run: | make -j8 + env: + CFLAGS: "-Wall -Wextra" + + - name: ABI Check + run: | + cd tests && ./run-check-namespace + - name: Test run: | set -x @@ -123,6 +138,9 @@ jobs: ulimit -c unlimited CROSS_LIB="/usr/${{ matrix.config.host }}" make -j8 check LOG_DRIVER_FLAGS="--qemu-arch ${{ matrix.config.qemu }}" LDFLAGS="-L$CROSS_LIB/lib -static" QEMU_LD_PREFIX="$CROSS_LIB" + env: + UNW_DEBUG_LEVEL: 4 + - name: Show Logs if: ${{ failure() }} run: | diff --git a/src/native/external/libunwind/.github/workflows/CI-win.yml b/src/native/external/libunwind/.github/workflows/CI-win.yml index 7effb9b23af871..6490816fe6a138 100644 --- a/src/native/external/libunwind/.github/workflows/CI-win.yml +++ b/src/native/external/libunwind/.github/workflows/CI-win.yml @@ -1,5 +1,8 @@ name: CI - Windows +permissions: + contents: read + on: pull_request: paths: @@ -27,7 +30,7 @@ jobs: - {toolchain: Visual Studio 17 2022, arch: x64, server: 2022, TARGET: aarch64-linux-gnu} - {toolchain: Visual Studio 17 2022, arch: x64, server: 2022, TARGET: x86_64-linux-gnu} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build shell: cmd env: diff --git a/src/native/external/libunwind/.github/workflows/codeql-analysis.yml b/src/native/external/libunwind/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000000..f6a6e0dfbcc457 --- /dev/null +++ b/src/native/external/libunwind/.github/workflows/codeql-analysis.yml @@ -0,0 +1,41 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + # Runs automatically on the twelfth of every month at 16:26 + - cron: '26 16 12 * *' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: security-extended,security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/src/native/external/libunwind/.github/workflows/groom-issues.yml b/src/native/external/libunwind/.github/workflows/groom-issues.yml new file mode 100644 index 00000000000000..ab7b5bfa58c7c5 --- /dev/null +++ b/src/native/external/libunwind/.github/workflows/groom-issues.yml @@ -0,0 +1,25 @@ +# Automate stale iossue tagging and closing +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + stale-issue-label: "needs info" + days-before-issue-stale: -1 + days-before-issue-close: 60 + close-issue-message: > + Without additional information we're not able to resolve this issue. + Feel free to add more info or respond to any questions above and we + can reopen the case. Thanks for your contribution! + days-before-pr-stale: -1 + days-before-pr-close: -1 + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/native/external/libunwind/.gitignore b/src/native/external/libunwind/.gitignore index cd3e197c7a6f00..d59a6e3f73563a 100644 --- a/src/native/external/libunwind/.gitignore +++ b/src/native/external/libunwind/.gitignore @@ -15,6 +15,7 @@ Makefile.in INSTALL aclocal.m4 +m4/ autom4te.cache/ aux config.log @@ -77,7 +78,12 @@ tests/[GL]ia64-test-readonly tests/[GL]ia64-test-stack tests/ia64-test-dyn1 tests/ia64-test-sig +tests/ppc64-test-altivec +tests/ppc64-test-plt tests/[GL]x64-test-dwarf-expressions tests/x64-unwind-badjmp-signal-frame +tests/[GL]arm64-test-sve-signal +tests/aarch64-test-plt +tests/aarch64-test-frame-record tests/*.log tests/*.trs diff --git a/src/native/external/libunwind/CMakeLists.txt b/src/native/external/libunwind/CMakeLists.txt index 245a41bfbb35d5..8de31d12e32f59 100644 --- a/src/native/external/libunwind/CMakeLists.txt +++ b/src/native/external/libunwind/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 11) set(PKG_MAJOR "1") set(PKG_MINOR "6") -set(PKG_EXTRA "-rc1") +set(PKG_EXTRA "-rc2") set(PACKAGE_STRING "libunwind") set(PACKAGE_BUGREPORT "") diff --git a/src/native/external/libunwind/Makefile.am b/src/native/external/libunwind/Makefile.am index 0d5eee057fb381..0ce5128af4d1a7 100644 --- a/src/native/external/libunwind/Makefile.am +++ b/src/native/external/libunwind/Makefile.am @@ -8,7 +8,9 @@ endif BUILD_PTRACE if BUILD_COREDUMP include_HEADERS += include/libunwind-coredump.h endif BUILD_COREDUMP - +if BUILD_NTO +include_HEADERS += include/libunwind-nto.h +endif BUILD_NTO if ARCH_AARCH64 include_HEADERS += include/libunwind-aarch64.h endif @@ -24,9 +26,6 @@ endif if ARCH_MIPS include_HEADERS += include/libunwind-mips.h endif -if ARCH_TILEGX -include_HEADERS += include/libunwind-tilegx.h -endif if ARCH_X86 include_HEADERS += include/libunwind-x86.h endif @@ -85,8 +84,6 @@ noinst_HEADERS = include/dwarf.h include/dwarf_i.h include/dwarf-eh.h \ include/tdep-hppa/jmpbuf.h include/tdep-hppa/dwarf-config.h \ include/tdep-mips/libunwind_i.h \ include/tdep-mips/jmpbuf.h include/tdep-mips/dwarf-config.h \ - include/tdep-tilegx/libunwind_i.h \ - include/tdep-tilegx/jmpbuf.h include/tdep-tilegx/dwarf-config.h \ include/tdep-x86/libunwind_i.h \ include/tdep-x86/jmpbuf.h include/tdep-x86/dwarf-config.h \ include/tdep-x86_64/libunwind_i.h \ diff --git a/src/native/external/libunwind/README b/src/native/external/libunwind/README index 26e1a9607c637c..62b0cdc84c7da1 100644 --- a/src/native/external/libunwind/README +++ b/src/native/external/libunwind/README @@ -16,7 +16,6 @@ This library supports several architecture/operating-system combinations: | Linux | SuperH | ✓ | | Linux | IA-64 | ✓ | | Linux | PARISC | Works well, but C library missing unwind-info | -| Linux | Tilegx | 64-bit mode only | | Linux | MIPS | ✓ | | Linux | RISC-V | 64-bit only | | Linux | LoongArch | 64-bit only | @@ -26,6 +25,8 @@ This library supports several architecture/operating-system combinations: | FreeBSD | AArch64 | ✓ | | FreeBSD | PPC32 | ✓ | | FreeBSD | PPC64 | ✓ | +| QNX | Aarch64 | ✓ | +| QNX | x86-64 | ✓ | | Solaris | x86-64 | ✓ | ## Libc Requirements @@ -52,7 +53,6 @@ such dependencies | riscv | p | p | | s390x | p | p | | sh | r | | -| tilegx | r | r | | x86 | p | r | | x86_64 | p | p | @@ -200,10 +200,5 @@ commands: ## Contacting the Developers -Please direct all questions regarding this library to . - -You can do this by sending an email to with -a body of "subscribe libunwind-devel", or you can subscribe and manage your -subscription via the web-interface at . - -You can also interact on our GitHub page: . +Please raise issues and pull requests through the GitHub repository: +. diff --git a/src/native/external/libunwind/SECURITY.md b/src/native/external/libunwind/SECURITY.md new file mode 100644 index 00000000000000..27b3483608d50f --- /dev/null +++ b/src/native/external/libunwind/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/libunwind/libunwind/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. diff --git a/src/native/external/libunwind/configure.ac b/src/native/external/libunwind/configure.ac index d97e50652d349f..98d71e16887497 100644 --- a/src/native/external/libunwind/configure.ac +++ b/src/native/external/libunwind/configure.ac @@ -1,10 +1,10 @@ dnl Process this file with autoconf to produce a configure script. define(pkg_major, 1) -define(pkg_minor, 7) +define(pkg_minor, 8) define(pkg_extra, 0) -define(pkg_maintainer, libunwind-devel@nongnu.org) -define(mkvers, $1.$2$3) +define(pkg_maintainer, https://github.com/libunwind/libunwind) +define(mkvers, $1.$2.$3) AC_INIT([libunwind],[mkvers(pkg_major, pkg_minor, pkg_extra)],[pkg_maintainer]) AC_CONFIG_SRCDIR(src/mi/backtrace.c) @@ -26,31 +26,22 @@ LT_INIT AM_PROG_AS AM_PROG_CC_C_O -dnl Checks for libraries. -AC_CHECK_LIB(uca, __uc_get_grs) -OLD_LIBS=${LIBS} -AC_SEARCH_LIBS(dlopen, dl) -LIBS=${OLD_LIBS} -case "$ac_cv_search_dlopen" in - -l*) DLLIB=$ac_cv_search_dlopen;; - *) DLLIB="";; -esac +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_CHECK_SIZEOF(off_t) dnl Checks for header files. -AC_HEADER_STDC +AC_MSG_NOTICE([--- Checking for header files ---]) AC_CHECK_HEADERS(asm/ptrace_offsets.h asm/ptrace.h asm/vsyscall.h endian.h sys/endian.h \ sys/param.h execinfo.h ia64intrin.h sys/uc_access.h unistd.h signal.h \ sys/types.h sys/procfs.h sys/ptrace.h sys/syscall.h byteswap.h elf.h \ sys/elf.h link.h sys/link.h) -dnl Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_C_INLINE -AC_TYPE_SIZE_T -AC_CHECK_SIZEOF(off_t) - CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE" +AC_MSG_NOTICE([--- Checking for available types ---]) AC_CHECK_MEMBERS([struct dl_phdr_info.dlpi_subs],,,[#include ]) AC_CHECK_TYPES([struct elf_prstatus, struct prstatus, procfs_status, elf_fpregset_t], [], [], [$ac_includes_default @@ -59,21 +50,24 @@ AC_CHECK_TYPES([struct elf_prstatus, struct prstatus, procfs_status, elf_fpregse #endif ]) -AC_CHECK_DECLS([PTRACE_POKEUSER, PTRACE_POKEDATA, PTRACE_SETREGSET, -PTRACE_TRACEME, PTRACE_CONT, PTRACE_SINGLESTEP, -PTRACE_SYSCALL, PT_IO, PT_GETREGS, -PT_GETFPREGS, PT_CONTINUE, PT_TRACE_ME, -PT_STEP, PT_SYSCALL], [], [], -[$ac_includes_default -#if HAVE_SYS_TYPES_H -#include -#endif -#include -]) +dnl Checks for libraries. +AC_MSG_NOTICE([--- Checking for libraries ---]) +save_LDFLAGS="$LDFLAGS" +save_LIBS="$LIBS" +LDFLAGS="${LDFLAGS} -nostdlib" +AC_SEARCH_LIBS([_Unwind_Resume], [gcc_s gcc], + [AS_IF([test "$ac_cv_search__Unwind_Resume" != "none required"], + [AC_SUBST([LIBCRTS], ["$ac_cv_search__Unwind_Resume"])])], + [], + [-lc] +) +LIBS="$save_LIBS" +LDFLAGS="$save_LDFLAGS" +AC_SEARCH_LIBS([__uc_get_grs], [uca]) dnl Checks for library functions. AC_CHECK_FUNCS(dl_iterate_phdr dl_phdr_removals_counter dlmodinfo getunwind \ - ttrace mincore pipe2 sigaltstack) + ttrace mincore pipe2 sigaltstack execvpe) AC_MSG_CHECKING([if building with AltiVec]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ @@ -100,7 +94,6 @@ AC_DEFUN([SET_ARCH],[ [powerpc*],[$2=ppc$ppc_bits], [sh*],[$2=sh], [amd64],[$2=x86_64], - [tile*],[$2=tilegx], [riscv*],[$2=riscv], [loongarch64*],[$2=loongarch64], [$2=$1]) @@ -110,61 +103,212 @@ SET_ARCH([$build_cpu],[build_arch]) SET_ARCH([$host_cpu],[host_arch]) SET_ARCH([$target_cpu],[target_arch]) -# Check for Android -AC_MSG_CHECKING([for Android]) -android="no" -case "$host_os" in - *android*) - android="yes" - AC_MSG_RESULT([yes]) - ;; - *) - AC_MSG_RESULT([no]) - ;; -esac - -AC_ARG_ENABLE(coredump, - AS_HELP_STRING([--enable-coredump],[building libunwind-coredump library]),, - [AS_CASE([$host_arch], [aarch64*|arm*|mips*|sh*|x86*|tile*|riscv*|loongarch64], [enable_coredump=yes], [enable_coredump=no])] +AC_MSG_CHECKING([if libunwind-coredump should be built]) +AC_ARG_ENABLE([coredump], + [AS_HELP_STRING([--enable-coredump], + [build libunwind-coredump library + @<:@default=autodetect@:>@])], + [], + [enable_coredump="check"] +) +AS_IF([test "$enable_coredump" = "check"], + [AS_CASE([$host_arch], + [aarch64*|arm*|mips*|sh*|x86*|riscv*|loongarch64], [enable_coredump=yes], + [enable_coredump=no])] ) - -AC_MSG_CHECKING([if we should build libunwind-coredump]) AC_MSG_RESULT([$enable_coredump]) +AM_CONDITIONAL(BUILD_COREDUMP, test x$enable_coredump = xyes) -AC_ARG_ENABLE(ptrace, - AS_HELP_STRING([--enable-ptrace],[building libunwind-ptrace library]),, - [AC_CHECK_HEADER([sys/ptrace.h], [enable_ptrace=yes], [enable_ptrace=no])] +AC_MSG_CHECKING([if libunwind-ptrace should be built]) +AC_ARG_ENABLE([ptrace], + [AS_HELP_STRING([--enable-ptrace], + [build libunwind-ptrace library + @<:@default=autodetect@:>@])], + [], + [enable_ptrace="check"] +) +AS_IF([test "$enable_ptrace" != "no"], + [AS_IF([test "$ac_cv_header_sys_ptrace_h" = "yes"], [enable_ptrace=yes], + [test "$enable_ptrace" != "check"], [AC_MSG_FAILURE([--enable-ptrace given but + ptrace not supported on target])], + [enable_ptrace="no"] + )] ) - -AC_MSG_CHECKING([if we should build libunwind-ptrace]) AC_MSG_RESULT([$enable_ptrace]) +AM_CONDITIONAL([BUILD_PTRACE], [test x$enable_ptrace = xyes]) +AM_COND_IF([BUILD_PTRACE], [ + AC_MSG_NOTICE([--- Checking for ptrace symbols ---]) + AC_CHECK_DECLS([PTRACE_POKEUSER, PTRACE_POKEDATA, PTRACE_SETREGSET, + PTRACE_TRACEME, PTRACE_CONT, PTRACE_SINGLESTEP, + PTRACE_SYSCALL, PT_IO, PT_GETREGS, + PT_GETFPREGS, PT_CONTINUE, PT_TRACE_ME, + PT_STEP, PT_SYSCALL], + [], + [], + [$ac_includes_default + #if HAVE_SYS_TYPES_H + #include + #endif + #include + ]) +]) -AC_ARG_ENABLE(setjmp, - AS_HELP_STRING([--enable-setjmp],[building libunwind-setjmp library]),, - [AS_IF([test x$target_arch = x$host_arch], [enable_setjmp=yes], [enable_setjmp=no])] +AC_MSG_CHECKING([if libunwind-nto should be built]) +AC_ARG_ENABLE(nto, + [AS_HELP_STRING([--enable-nto], + [build libunwind-nto library + @<:@default=autodetect@:>@])], + [], + [enable_nto="check"] +) +AS_IF([test "$enable_nto" != "no"], + [AC_CHECK_HEADER([sys/neutrino.h], [enable_nto=yes], [enable_nto=no])] +) +AC_MSG_RESULT([$enable_nto]) +AM_CONDITIONAL([BUILD_NTO], [test x$enable_nto = xyes]) + +AC_MSG_CHECKING([if libunwind-setjmp should be built]) +AC_ARG_ENABLE([setjmp], + [AS_HELP_STRING([--enable-setjmp], + [build libunwind-setjmp library + @<:@default=autodetect@:>@])], + [], + [enable_setjmp=check] ) +AS_IF([test "$enable_setjmp" = check], + [AS_IF([test x$target_arch = x$host_arch], + [enable_setjmp=yes], + [enable_setjmp=no])] + [AS_IF([expr x$target_os : xnto-qnx >/dev/null], + [enable_setjmp=no])] +) +AC_MSG_RESULT([$enable_setjmp]) +AM_CONDITIONAL(BUILD_SETJMP, test x$enable_setjmp = xyes) -AC_ARG_ENABLE(documentation, - AS_HELP_STRING([--disable-documentation],[Disable generating the man pages]),, - [enable_documentation=yes]) +AC_MSG_CHECKING([if weak-backtrace is enabled]) +AC_ARG_ENABLE([weak-backtrace], + [AS_HELP_STRING([--disable-weak-backtrace], + [do not provide the weak 'backtrace' symbol + @<:@default=no@:>@])], + [], + [enable_weak_backtrace=yes] +) +AC_MSG_RESULT([$enable_weak_backtrace]) +AM_CONDITIONAL([CONFIG_WEAK_BACKTRACE], [test "x$enable_weak_backtrace" = xyes]) +AM_COND_IF([CONFIG_WEAK_BACKTRACE], [ + AC_DEFINE([CONFIG_WEAK_BACKTRACE], [1], [Define if the weak 'backtrace' symbol is provided.]) +]) -AC_ARG_ENABLE(tests, - AS_HELP_STRING([--disable-tests],[Disable tests build]),, - [enable_tests=yes]) -AC_ARG_ENABLE(weak-backtrace, - AS_HELP_STRING([--disable-weak-backtrace],[Do not provide the weak 'backtrace' symbol.]),, - [enable_weak_backtrace=yes]) +AC_MSG_CHECKING([if unwind.h should be exported]) +AC_ARG_ENABLE([unwind-header], + [AS_HELP_STRING([--disable-unwind-header], + [do not export the 'unwind.h' header + @<:@default=no@:>@])], + [], + [enable_unwind_header=yes] +) +AC_MSG_RESULT([$enable_unwind_header]) +AM_CONDITIONAL(BUILD_UNWIND_HEADER, test "x$enable_unwind_header" = xyes) -AC_ARG_ENABLE(unwind-header, - AS_HELP_STRING([--disable-unwind-header],[Do not export the 'unwind.h' header]),, - [enable_unwind_header=yes]) +AC_MSG_CHECKING([whether to support UNW_CACHE_PER_THREAD]) +AC_ARG_ENABLE([per-thread-cache], + [AS_HELP_STRING([--enable-per-thread-cache], + [build with support for UNW_CACHE_PER_THREAD + (which imposes a high TLS memory usage) + @<:@default=no@:>@])], + [], + [enable_per_thread_cache=no] +) +AC_MSG_RESULT([$enable_per_thread_cache]) +AS_IF([test x$enable_per_thread_cache = xyes], + [AC_DEFINE([HAVE___CACHE_PER_THREAD], 1, [Define to 1 if --enable-per-thread-cache])] +) -AC_MSG_CHECKING([if we should export unwind.h]) -AC_MSG_RESULT([$enable_unwind_header]) +AC_MSG_CHECKING([if testsuite should be built]) +AC_ARG_ENABLE([tests], + [AS_HELP_STRING([--disable-tests], + [disable building tests @<:@default=no@:>@])], + [], + [enable_tests=yes] +) +AC_MSG_RESULT([$enable_tests]) +AM_CONDITIONAL([CONFIG_TESTS], [test x$enable_tests = xyes]) +AM_COND_IF([CONFIG_TESTS], [ + old_LIBS="$LIBS" + AC_MSG_NOTICE([--- Checking for extra libraries linked to tests ---]) + AC_SEARCH_LIBS([dlopen], [dl], + [AS_IF([test "$ac_cv_search_dlopen" != "none required"], + [AC_SUBST([DLLIB], ["$ac_cv_search_dlopen"])])]) + AC_SEARCH_LIBS([pthread_create], [pthread], + [AS_IF([test "$ac_cv_search_pthread_create" != "none required"], + [AC_SUBST([PTHREADS_LIB],["$ac_cv_search_pthread_create"])])]) + AC_SEARCH_LIBS([backtrace], [execinfo], + [AS_IF([test "$ac_cv_search_backtrace" != "none required"], + [AC_SUBST([BACKTRACELIB],["$ac_cv_search_backtrace"])])]) + LIBS="$old_LIBS" + AC_CONFIG_FILES(tests/Makefile tests/check-namespace.sh) +]) +AC_ARG_WITH([testdriver], + [AS_HELP_STRING([--with-testdriver], + [use designated test driver instead of default LOG_DRIVER])], + [], + [with_testdriver=\$\(top_srcdir\)/config/test-driver]) +AC_SUBST([UNW_TESTDRIVER], $with_testdriver) -AC_MSG_CHECKING([if we should build libunwind-setjmp]) -AC_MSG_RESULT([$enable_setjmp]) + +AC_MSG_CHECKING([if debug support should be built]) +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [enable debug support (slows down execution) + @<:@default=no@:>@])], + [], + [enable_debug=no] +) +AC_MSG_RESULT([$enable_debug]) +AS_IF([test x$enable_debug = xyes], + [CPPFLAGS="${CPPFLAGS} -DDEBUG"], + [CPPFLAGS="${CPPFLAGS} -DNDEBUG"] +) + +AC_MSG_CHECKING([if C++ exception support should be built]) +AC_ARG_ENABLE([cxx_exceptions], + [AS_HELP_STRING([--enable-cxx-exceptions], + [use libunwind to handle C++ exceptions + @<:@default=autodetect@:>@])], + [], + [enable_cxx_exceptions=check] +) +AS_IF([test $enable_cxx_exceptions = check], + [AS_CASE([$target_arch], + [aarch64*|arm*|mips*|x86*|s390x*|loongarch64], [enable_cxx_exceptions=no], + [enable_cxx_exceptions=yes])] +) +AC_MSG_RESULT([$enable_cxx_exceptions]) +AM_CONDITIONAL([SUPPORT_CXX_EXCEPTIONS], [test x$enable_cxx_exceptions = xyes]) + +AC_MSG_CHECKING([if documentation should be built]) +AC_ARG_ENABLE([documentation], + [AS_HELP_STRING([--enable-documentation], + [enable generating the man pages @<:@default=yes@:>@])], + [], + [enable_documentation=yes]) +AC_MSG_RESULT([$enable_documentation]) +AC_PATH_PROG([LATEX2MAN],[latex2man]) +AS_IF([test "x$LATEX2MAN" = "x" && test "x$enable_documentation" != "xno"], [ + AC_MSG_WARN([latex2man not found. Install latex2man. Disabling docs.]) + enable_documentation="no"; +]) +AM_CONDITIONAL([CONFIG_DOCS], [test x$enable_documentation != xno]) +AM_COND_IF([CONFIG_DOCS], [AC_CONFIG_FILES([doc/Makefile doc/common.tex])]) + +# Enable tests built around unw_resume, which is not supported on all targets +AC_MSG_CHECKING([if we should enable unw_resume tests]) +AS_CASE([$target_os], + [nto-qnx*], [enable_unw_resume_tests=no], + [enable_unw_resume_tests=yes]) +AC_MSG_RESULT([$enable_unw_resume_tests]) +AM_CONDITIONAL([ENABLE_UNW_RESUME_TESTS], [test x$enable_unw_resume_tests = xyes]) AC_MSG_CHECKING([for build architecture]) AC_MSG_RESULT([$build_arch]) @@ -175,11 +319,8 @@ AC_MSG_RESULT([$target_arch]) AC_MSG_CHECKING([for target operating system]) AC_MSG_RESULT([$target_os]) -AM_CONDITIONAL(BUILD_COREDUMP, test x$enable_coredump = xyes) -AM_CONDITIONAL(BUILD_PTRACE, test x$enable_ptrace = xyes) -AM_CONDITIONAL(BUILD_SETJMP, test x$enable_setjmp = xyes) -AM_CONDITIONAL(BUILD_UNWIND_HEADER, test "x$enable_unwind_header" = xyes) -AM_CONDITIONAL(NO_PTRACE_TEST, test x$build_arch != x$host_arch) +AM_CONDITIONAL([XFAIL_PTRACE_TEST], [echo $CFLAGS | grep -q '\-m32\>']) +AM_CONDITIONAL([CROSS_BUILD], [test x$build_arch != x$host_arch]) AM_CONDITIONAL(REMOTE_ONLY, test x$target_arch != x$host_arch) AM_CONDITIONAL(ARCH_AARCH64, test x$target_arch = xaarch64) AM_CONDITIONAL(ARCH_ARM, test x$target_arch = xarm) @@ -191,7 +332,6 @@ AM_CONDITIONAL(ARCH_X86_64, test x$target_arch = xx86_64) AM_CONDITIONAL(ARCH_PPC32, test x$target_arch = xppc32) AM_CONDITIONAL(ARCH_PPC64, test x$target_arch = xppc64) AM_CONDITIONAL(ARCH_SH, test x$target_arch = xsh) -AM_CONDITIONAL(ARCH_TILEGX, test x$target_arch = xtilegx) AM_CONDITIONAL(ARCH_S390X, test x$target_arch = xs390x) AM_CONDITIONAL(ARCH_RISCV, test x$target_arch = xriscv) AM_CONDITIONAL(ARCH_LOONGARCH64, test x$target_arch = xloongarch64) @@ -202,12 +342,12 @@ AM_CONDITIONAL(OS_QNX, expr x$target_os : xnto-qnx >/dev/null) AM_CONDITIONAL(OS_SOLARIS, expr x$target_os : xsolaris >/dev/null) AC_MSG_CHECKING([for ELF helper width]) -case "${target_arch}" in -(arm|hppa|ppc32|x86|sh) use_elf32=yes; AC_MSG_RESULT([32]);; -(aarch64|ia64|ppc64|x86_64|s390x|tilegx) use_elf64=yes; AC_MSG_RESULT([64]);; -(mips|riscv|loongarch64) use_elfxx=yes; AC_MSG_RESULT([xx]);; -*) AC_MSG_ERROR([Unknown ELF target: ${target_arch}]) -esac +AS_CASE([${target_arch}], + [arm|hppa|ppc32|x86|sh], [use_elf32=yes; AC_MSG_RESULT([32])], + [aarch64|ia64|ppc64|x86_64|s390x], [use_elf64=yes; AC_MSG_RESULT([64])], + [mips|riscv|loongarch64], [use_elfxx=yes; AC_MSG_RESULT([xx])], + [AC_MSG_ERROR([Unknown ELF target: ${target_arch}])] +) AM_CONDITIONAL(USE_ELF32, [test x$use_elf32 = xyes]) AM_CONDITIONAL(USE_ELF64, [test x$use_elf64 = xyes]) AM_CONDITIONAL(USE_ELFXX, [test x$use_elfxx = xyes]) @@ -230,36 +370,6 @@ else fi AC_MSG_RESULT([$remote_only]) -AC_MSG_CHECKING([whether to enable debug support]) -AC_ARG_ENABLE(debug, -AS_HELP_STRING([--enable-debug],[turn on debug support (slows down execution)])) -if test x$enable_debug = xyes; then - CPPFLAGS="${CPPFLAGS} -DDEBUG" -else - CPPFLAGS="${CPPFLAGS} -DNDEBUG" -fi -AC_MSG_RESULT([$enable_debug]) - -AC_MSG_CHECKING([whether to enable C++ exception support]) -AC_ARG_ENABLE(cxx_exceptions, -AS_HELP_STRING([--enable-cxx-exceptions],[use libunwind to handle C++ exceptions]),, -[ -# C++ exception handling doesn't work too well on x86 -case $target_arch in - x86*) enable_cxx_exceptions=no;; - aarch64*) enable_cxx_exceptions=no;; - arm*) enable_cxx_exceptions=no;; - mips*) enable_cxx_exceptions=no;; - tile*) enable_cxx_exceptions=no;; - s390x*) enable_cxx_exceptions=no;; - loongarch*) enable_cxx_exceptions=no;; - *) enable_cxx_exceptions=yes;; -esac -]) - -AM_CONDITIONAL([SUPPORT_CXX_EXCEPTIONS], [test x$enable_cxx_exceptions = xyes]) -AC_MSG_RESULT([$enable_cxx_exceptions]) - AC_MSG_CHECKING([whether to load .debug_frame sections]) AC_ARG_ENABLE(debug_frame, AS_HELP_STRING([--enable-debug-frame],[Load the ".debug_frame" section if available]),, [ @@ -337,15 +447,6 @@ fi AC_SUBST([LIBZ]) AM_CONDITIONAL(HAVE_ZLIB, test x$enable_zlibdebuginfo = xyes) -AC_MSG_CHECKING([whether to support UNW_CACHE_PER_THREAD]) -AC_ARG_ENABLE([per-thread-cache], -AS_HELP_STRING([--enable-per-thread-cache], [build with support for UNW_CACHE_PER_THREAD (which imposes a high TLS memory usage) (default: disabled)])) -AC_MSG_RESULT([$enable_per_thread_cache]) -AS_IF([test x$enable_per_thread_cache = xyes], [ - AC_DEFINE(HAVE___CACHE_PER_THREAD, 1, - [Define to 1 if --enable-per-thread-cache]) -]) - AC_MSG_CHECKING([for Intel compiler]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[#ifndef __INTEL_COMPILER #error choke me @@ -386,9 +487,13 @@ else LDFLAGS_NOSTARTFILES="-XCClinker -nostartfiles" fi -if test x$GCC = xyes -a x$intel_compiler != xyes -a x$qcc_compiler != xyes -a x$android != xyes; then - LIBCRTS="-lgcc_s" -fi +OLD_CFLAGS="${CFLAGS}" +CFLAGS="${CFLAGS} -march=armv8-a+sve" +AC_MSG_CHECKING([if compiler supports -march=armv8-a+sve]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [supports_march_armv8_a_sve=yes],[supports_march_armv8_a_sve=no]) +AM_CONDITIONAL(COMPILER_SUPPORTS_MARCH_ARMV8_A_SVE, [test x$supports_march_armv8_a_sve = xyes]) +AC_MSG_RESULT([$supports_march_armv8_a_sve]) +CFLAGS="${OLD_CFLAGS}" AC_MSG_CHECKING([for __builtin___clear_cache]) AC_LINK_IFELSE( @@ -423,21 +528,6 @@ PKG_MINOR=pkg_minor PKG_EXTRA=pkg_extra PKG_MAINTAINER=pkg_maintainer -old_LIBS="$LIBS" -LIBS="" -AC_SEARCH_LIBS(backtrace, execinfo) -LIBS="$old_LIBS" -case "$ac_cv_search_backtrace" in - -l*) BACKTRACELIB=$ac_cv_search_backtrace;; - *) BACKTRACELIB="";; -esac - -AC_ARG_WITH([testdriver], - [AS_HELP_STRING([--with-testdriver], - [use designated test driver instead of default LOG_DRIVER])], - [], - [with_testdriver=\$\(top_srcdir\)/config/test-driver]) -AC_SUBST([UNW_TESTDRIVER], $with_testdriver) AC_SUBST(build_arch) AC_SUBST(target_os) @@ -445,36 +535,13 @@ AC_SUBST(arch) AC_SUBST(ARCH) AC_SUBST(LDFLAGS_STATIC_LIBCXA) AC_SUBST(LDFLAGS_NOSTARTFILES) -AC_SUBST(LIBCRTS) AC_SUBST(PKG_MAJOR) AC_SUBST(PKG_MINOR) AC_SUBST(PKG_EXTRA) AC_SUBST(PKG_MAINTAINER) AC_SUBST(enable_cxx_exceptions) AC_SUBST(enable_debug_frame) -AC_SUBST(DLLIB) -AC_SUBST(BACKTRACELIB) -AC_PATH_PROG([LATEX2MAN],[latex2man]) -if test "x$LATEX2MAN" = "x" && test "x$enable_documentation" = "xyes"; then - AC_MSG_WARN([latex2man not found. Install latex2man. Disabling docs.]) - enable_documentation="no"; -fi - -AM_CONDITIONAL([CONFIG_DOCS], [test x$enable_documentation = xyes]) -if test "x$enable_documentation" = "xyes"; then - AC_CONFIG_FILES(doc/Makefile doc/common.tex) -fi - -AM_CONDITIONAL([CONFIG_TESTS], [test x$enable_tests = xyes]) -if test "x$enable_tests" = "xyes"; then - AC_CONFIG_FILES(tests/Makefile tests/check-namespace.sh) -fi - -AM_CONDITIONAL([CONFIG_WEAK_BACKTRACE], [test "x$enable_weak_backtrace" = xyes]) -AM_COND_IF([CONFIG_WEAK_BACKTRACE], [ - AC_DEFINE([CONFIG_WEAK_BACKTRACE], [1], [Define if the weak 'backtrace' symbol is provided.]) -]) AC_CONFIG_FILES(Makefile src/Makefile include/libunwind-common.h diff --git a/src/native/external/libunwind/doc/Makefile.am b/src/native/external/libunwind/doc/Makefile.am index 2252978dcd79fc..74bf6ca6a70684 100644 --- a/src/native/external/libunwind/doc/Makefile.am +++ b/src/native/external/libunwind/doc/Makefile.am @@ -1,6 +1,9 @@ # man pages that go into section 3: man3_MANS = libunwind.man libunwind-dynamic.man libunwind-ia64.man \ - libunwind-ptrace.man libunwind-setjmp.man \ + libunwind-coredump.man \ + libunwind-ptrace.man \ + libunwind-setjmp.man \ + libunwind-nto.man \ unw_apply_reg_state.man \ unw_backtrace.man \ unw_flush_cache.man \ @@ -22,17 +25,23 @@ man3_MANS = libunwind.man libunwind-dynamic.man libunwind-ia64.man \ unw_regname.man unw_resume.man \ unw_reg_states_iterate.man \ unw_set_caching_policy.man \ + unw_set_iterate_phdr_function.man \ unw_set_cache_size.man \ unw_set_fpreg.man \ unw_set_reg.man \ unw_step.man \ unw_strerror.man \ _U_dyn_register.man \ - _U_dyn_cancel.man + _U_dyn_cancel.man \ + unw_get_elf_filename.man \ + unw_get_elf_filename_by_ip.man EXTRA_DIST = NOTES libunwind.trans \ libunwind.tex libunwind-dynamic.tex libunwind-ia64.tex \ - libunwind-ptrace.tex libunwind-setjmp.tex \ + libunwind-coredump.tex \ + libunwind-ptrace.tex \ + libunwind-setjmp.tex \ + libunwind-nto.tex \ unw_apply_reg_state.tex \ unw_backtrace.tex \ unw_flush_cache.tex \ @@ -50,6 +59,7 @@ EXTRA_DIST = NOTES libunwind.trans \ unw_is_signal_frame.tex \ unw_create_addr_space.tex unw_destroy_addr_space.tex \ unw_regname.tex unw_resume.tex unw_set_caching_policy.tex \ + unw_set_iterate_phdr_function.tex \ unw_reg_states_iterate.tex \ unw_set_cache_size.tex \ unw_set_fpreg.tex \ @@ -58,6 +68,8 @@ EXTRA_DIST = NOTES libunwind.trans \ unw_strerror.tex \ _U_dyn_register.tex \ _U_dyn_cancel.tex \ + unw_get_elf_filename.tex \ + unw_get_elf_filename_by_ip.tex \ $(man3_MANS) L2M = latex2man diff --git a/src/native/external/libunwind/doc/_U_dyn_cancel.man b/src/native/external/libunwind/doc/_U_dyn_cancel.man index a420a6deaf3e09..653b09795ae7be 100644 --- a/src/native/external/libunwind/doc/_U_dyn_cancel.man +++ b/src/native/external/libunwind/doc/_U_dyn_cancel.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:49 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "\\_U\\_DYN\\_CANCEL" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "\\_U\\_DYN\\_CANCEL" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME _U_dyn_cancel \-\- cancel unwind\-info for dynamically generated code @@ -30,7 +32,7 @@ _U_dyn_cancel(unw_dyn_info_t *di); .PP The _U_dyn_cancel() routine cancels the registration of the -unwind\-info for a dynamically generated procedure. Argument di +unwind info for a dynamically generated procedure. Argument di is the pointer to the unw_dyn_info_t structure that describes the procedure\&'s unwind\-info. @@ -45,15 +47,15 @@ or _U_dyn_cancel()). .PP _U_dyn_cancel() -is thread\-safe but \fInot\fP +is thread safe but \fInot\fP safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind\-dynamic(3), -_U_dyn_register(3) +libunwind\-dynamic(3libunwind), +_U_dyn_register(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/_U_dyn_cancel.tex b/src/native/external/libunwind/doc/_U_dyn_cancel.tex index ca5a12a76eede5..c9cefc3e18a2d1 100644 --- a/src/native/external/libunwind/doc/_U_dyn_cancel.tex +++ b/src/native/external/libunwind/doc/_U_dyn_cancel.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{\_U\_dyn\_cancel}{David Mosberger-Tang}{Programming Library}{\_U\_dyn\_cancel}\_U\_dyn\_cancel -- cancel unwind-info for dynamically generated code +\begin{Name}{3libunwind}{\_U\_dyn\_cancel}{David Mosberger-Tang}{Programming Library}{\_U\_dyn\_cancel}\_U\_dyn\_cancel -- cancel unwind-info for dynamically generated code \end{Name} \section{Synopsis} @@ -17,7 +17,7 @@ \section{Synopsis} \section{Description} The \Func{\_U\_dyn\_cancel}() routine cancels the registration of the -unwind-info for a dynamically generated procedure. Argument \Var{di} +unwind info for a dynamically generated procedure. Argument \Var{di} is the pointer to the \Type{unw\_dyn\_info\_t} structure that describes the procedure's unwind-info. @@ -28,12 +28,13 @@ \section{Description} \section{Thread and Signal Safety} -\Func{\_U\_dyn\_cancel}() is thread-safe but \emph{not} safe to use +\Func{\_U\_dyn\_cancel}() is thread safe but \emph{not} safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind-dynamic(3)}, \SeeAlso{\_U\_dyn\_register(3)} +\SeeAlso{libunwind-dynamic}(3libunwind), +\SeeAlso{\_U\_dyn\_register}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/_U_dyn_register.man b/src/native/external/libunwind/doc/_U_dyn_register.man index 107e5fd0e18d8c..dc5814160d8156 100644 --- a/src/native/external/libunwind/doc/_U_dyn_register.man +++ b/src/native/external/libunwind/doc/_U_dyn_register.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:49 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,10 +12,10 @@ .fi .. -.TH "\\_U\\_DYN\\_REGISTER" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "\\_U\\_DYN\\_REGISTER" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME _U_dyn_register -\-\- register unwind\-info for dynamically generated code +\-\- register unwind info for dynamically generated code .PP .SH SYNOPSIS @@ -29,11 +31,11 @@ _U_dyn_register(unw_dyn_info_t *di); .PP The _U_dyn_register() -routine registers unwind\-info for a -dynamically generated procedure. The procedure\&'s unwind\-info is +routine registers unwind info for a +dynamically generated procedure. The procedure\&'s unwind info is described by a structure of type unw_dyn_info_t (see -libunwind\-dynamic(3)). +libunwind\-dynamic(3libunwind)). A pointer to this structure is passed in argument di\&. .PP @@ -47,15 +49,15 @@ or _U_dyn_cancel()). .PP _U_dyn_register() -is thread\-safe but \fInot\fP +is thread safe but \fInot\fP safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind\-dynamic(3), -_U_dyn_cancel(3) +libunwind\-dynamic(3libunwind), +_U_dyn_cancel(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/_U_dyn_register.tex b/src/native/external/libunwind/doc/_U_dyn_register.tex index ab23b5c6213634..d56a7ceec81c65 100644 --- a/src/native/external/libunwind/doc/_U_dyn_register.tex +++ b/src/native/external/libunwind/doc/_U_dyn_register.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{\_U\_dyn\_register}{David Mosberger-Tang}{Programming Library}{\_U\_dyn\_register}\_U\_dyn\_register -- register unwind-info for dynamically generated code +\begin{Name}{3libunwind}{\_U\_dyn\_register}{David Mosberger-Tang}{Programming Library}{\_U\_dyn\_register}\_U\_dyn\_register -- register unwind info for dynamically generated code \end{Name} \section{Synopsis} @@ -16,10 +16,10 @@ \section{Synopsis} \section{Description} -The \Func{\_U\_dyn\_register}() routine registers unwind-info for a -dynamically generated procedure. The procedure's unwind-info is +The \Func{\_U\_dyn\_register}() routine registers unwind info for a +dynamically generated procedure. The procedure's unwind info is described by a structure of type \Type{unw\_dyn\_info\_t} (see -\SeeAlso{libunwind-dynamic(3)}). A pointer to this structure is +\SeeAlso{libunwind-dynamic}(3libunwind)). A pointer to this structure is passed in argument \Var{di}. The \Func{\_U\_dyn\_register}() routine is guaranteed to execute in @@ -29,12 +29,13 @@ \section{Description} \section{Thread and Signal Safety} -\Func{\_U\_dyn\_register}() is thread-safe but \emph{not} safe to use +\Func{\_U\_dyn\_register}() is thread safe but \emph{not} safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind-dynamic(3)}, \SeeAlso{\_U\_dyn\_cancel(3)} +\SeeAlso{libunwind-dynamic}(3libunwind), +\SeeAlso{\_U\_dyn\_cancel}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/libunwind-coredump.man b/src/native/external/libunwind/doc/libunwind-coredump.man new file mode 100644 index 00000000000000..9562ea5fbac9a2 --- /dev/null +++ b/src/native/external/libunwind/doc/libunwind-coredump.man @@ -0,0 +1,211 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} +'\" t +.\" Manual page created with latex2man on Tue Aug 29 10:53:41 2023 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "LIBUNWIND\-COREDUMP" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " +.SH NAME +libunwind\-coredump +\-\- coredump() support in libunwind +.PP +.SH SYNOPSIS + +.PP +#include +.br +.PP +unw_accessors_t +_UCD_accessors; +.br +.PP +struct UCD_info *_UCD_create(char const *); +.br +void _UCD_destroy(struct UCD_info *); +.br +.PP +int +_UCD_get_num_threads(struct UCD_info *); +.br +void +_UCD_select_thread(struct UCD_info *, +int); +.br +void +_UCD_get_pid(struct UCD_info *); +.br +void +_UCD_get_cursig(struct UCD_info *); +.br +.PP +int +_UCD_find_proc_info(unw_addr_space_t, +unw_word_t, +unw_proc_info_t *, +int, +void *); +.br +void +_UCD_put_unwind_info(unw_addr_space_t, +unw_proc_info_t *, +void *); +.br +int +_UCD_get_dyn_info_list_addr(unw_addr_space_t, +unw_word_t *, +void *); +.br +int +_UCD_access_mem(unw_addr_space_t, +unw_word_t, +unw_word_t *, +int, +void *); +.br +int +_UCD_access_reg(unw_addr_space_t, +unw_regnum_t, +unw_word_t *, +int, +void *); +.br +int +_UCD_access_fpreg(unw_addr_space_t, +unw_regnum_t, +unw_fpreg_t *, +int, +void *); +.br +int +_UCD_get_proc_name(unw_addr_space_t, +unw_word_t, +char *, +size_t, +unw_word_t *, +void *); +.br +int +_UCD_resume(unw_addr_space_t, +unw_cursor_t *, +void *); +.br +.PP +.SH DESCRIPTION + +.PP +It is possible to generate a snapshot of a process state at a specific moment in +time and save it in a specially\-formatted file called a coredump. +This often happens automatically when a process encounters an unrecoverable +error and the OS itself captures the state of the process when the error +occurred. +libunwind +provides a library that can be used as part of a lightweight +tool to generate some useful information as to why the process abnormally +terminated (such as a stack trace of al threads of execution). +The routines and variables +implementing this facility use a prefix of _UCD, +which +stands for ``unwind\-via\-coredump\&''\&. +.PP +An application that wants to use the coredump remote first needs +to create a new libunwind +address space that represents the +target process. This is done by calling +unw_create_addr_space(). +In many cases, the application +will simply want to pass the address of _UCD_accessors +as the +first argument to this routine. Doing so will ensure that +libunwind +will be able to properly unwind the target process. +However, in special circumstances, an application may prefer to use +only portions of the _UCD\-facility. +For this reason, the +individual callback routines (_UCD_find_proc_info(), +_UCD_put_unwind_info(), +etc.) are also available for direct +use. Of course, the addresses of these routines could also be picked +up from _UCD_accessors, +but doing so would prevent static +initialization. Also, when using _UCD_accessors, +\fIall\fP +the callback routines will be linked into the application, even if +they are never actually called. +.PP +Next, the application needs to load the corefile for analysis and create an +(opaque) UCD_info structure by calling _UCD_create(), +passing the name of the corefile. +The returned opaque pointer then needs to be +passed as the ``argument\&'' pointer (third argument) to +unw_init_remote(). +.PP +When the application is done using libunwind +on the corefile, +_UCD_destroy() +needs to be called, +passing it the pointer that was returned by the corresponding call to +_UCD_create(). +This ensures that all memory and other resources are freed up. +.PP +.TP +_UCD_get_num_threads() + Gets the number of threads in the corefile. +.PP +.TP +_UCD_get_pid() + Gets the process ID of the process associated with the corefile. +.PP +.TP +_UCD_get_cursig() + Gets the current signal begin received by the process associated with the +corefile (if any). +.PP +.TP +_UCD_select_thread() + Selects the current thread for unwinding. +.PP +.SH THREAD SAFETY + +.PP +The coredump remote assumes that a single _UCD_info +structure is never shared between threads. +Because of this, +no explicit locking is used. +As long as only one thread uses a _UCD_info +structure at any given time, +this facility is thread\-safe. +.PP +.SH RETURN VALUE + +.PP +_UCD_create() +may return a null pointer if it fails +to create the UCD_info +for any reason. +.PP +.SH FILES + +.PP +.TP +libunwind\-coredump.h + Header file to include when using the +interface defined by this library. +.TP +\fB\-l\fPunwind\-coredump \fB\-l\fPunwind\-generic + Linker\-switches to add when building a program that uses the +functions defined by this library. +.PP +.SH SEE ALSO + +.PP +libunwind(3libunwind) +.PP +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/src/native/external/libunwind/doc/libunwind-coredump.tex b/src/native/external/libunwind/doc/libunwind-coredump.tex new file mode 100644 index 00000000000000..08b5e0055b749f --- /dev/null +++ b/src/native/external/libunwind/doc/libunwind-coredump.tex @@ -0,0 +1,138 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3libunwind}{libunwind-coredump}{}{Programming Library}{coredump analysis support in libunwind}libunwind-coredump -- coredump() support in libunwind +\end{Name} + +\section{Synopsis} + +\File{\#include $<$libunwind-coredump.h$>$}\\ + +\noindent +\Type{unw\_accessors\_t} \Var{\_UCD\_accessors};\\ + +\Type{struct~UCD\_info~*}\Func{\_UCD\_create}(\Type{char const~*});\\ +\noindent +\Type{void}~\Func{\_UCD\_destroy}(\Type{struct UCD\_info~*});\\ + +\noindent +\Type{int} \Func{\_UCD\_get\_num\_threads}(\Type{struct UCD\_info~*});\\ +\noindent +\Type{void} \Func{\_UCD\_select\_thread}(\Type{struct UCD\_info~*}, \Type{int});\\ +\noindent +\Type{void} \Func{\_UCD\_get\_pid}(\Type{struct UCD\_info~*});\\ +\noindent +\Type{void} \Func{\_UCD\_get\_cursig}(\Type{struct UCD\_info~*});\\ + +\noindent +\Type{int} \Func{\_UCD\_find\_proc\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_proc\_info\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{void} \Func{\_UCD\_put\_unwind\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_proc\_info\_t~*}, \Type{void~*});\\ +\noindent +\Type{int} \Func{\_UCD\_get\_dyn\_info\_list\_addr}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ +\noindent +\Type{int} \Func{\_UCD\_access\_mem}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{int} \Func{\_UCD\_access\_reg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{int} \Func{\_UCD\_access\_fpreg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_fpreg\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{int} \Func{\_UCD\_get\_proc\_name}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{char~*}, \Type{size\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ +\noindent +\Type{int} \Func{\_UCD\_resume}(\Type{unw\_addr\_space\_t}, \Type{unw\_cursor\_t~*}, \Type{void~*});\\ + +\section{Description} + +It is possible to generate a snapshot of a process state at a specific moment in +time and save it in a specially-formatted file called a coredump. +This often happens automatically when a process encounters an unrecoverable +error and the OS itself captures the state of the process when the error +occurred. +\Prog{libunwind} provides a library that can be used as part of a lightweight +tool to generate some useful information as to why the process abnormally +terminated (such as a stack trace of al threads of execution). +The routines and variables +implementing this facility use a prefix of \Func{\_UCD}, which +stands for ``unwind-via-coredump''. + +An application that wants to use the coredump remote first needs +to create a new \Prog{libunwind} address space that represents the +target process. This is done by calling +\Func{unw\_create\_addr\_space}(). In many cases, the application +will simply want to pass the address of \Var{\_UCD\_accessors} as the +first argument to this routine. Doing so will ensure that +\Prog{libunwind} will be able to properly unwind the target process. +However, in special circumstances, an application may prefer to use +only portions of the \Prog{\_UCD}-facility. For this reason, the +individual callback routines (\Func{\_UCD\_find\_proc\_info}(), +\Func{\_UCD\_put\_unwind\_info}(), etc.) are also available for direct +use. Of course, the addresses of these routines could also be picked +up from \Var{\_UCD\_accessors}, but doing so would prevent static +initialization. Also, when using \Var{\_UCD\_accessors}, \emph{all} +the callback routines will be linked into the application, even if +they are never actually called. + +Next, the application needs to load the corefile for analysis and create an +(opaque) UCD\_info structure by calling \Func{\_UCD_create}(), +passing the name of the corefile. +The returned opaque pointer then needs to be +passed as the ``argument'' pointer (third argument) to +\Func{unw\_init\_remote}(). + +When the application is done using \Prog{libunwind} on the corefile, +\Func{\_UCD\_destroy}() needs to be called, +passing it the pointer that was returned by the corresponding call to +\Func{\_UCD\_create}(). +This ensures that all memory and other resources are freed up. + +\begin{description}[style=nextline] + + \item[\Func{\_UCD\_get\_num\_threads}()] + Gets the number of threads in the corefile. + +\item[\Func{\_UCD\_get\_pid}()] + Gets the process ID of the process associated with the corefile. + +\item[\Func{\_UCD\_get\_cursig}()] + Gets the current signal begin received by the process associated with the + corefile (if any). + +\item[\Func{\_UCD\_select\_thread}()] + Selects the current thread for unwinding. + +\end{description} + +\section{Thread Safety} + +The coredump remote assumes that a single \Prog{\_UCD\_info} +structure is never shared between threads. +Because of this, +no explicit locking is used. +As long as only one thread uses a \Prog{\_UCD\_info} structure at any given time, +this facility is thread-safe. + +\section{Return Value} + +\Func{\_UCD\_create}() may return a null pointer if it fails +to create the \Prog{UCD\_info} for any reason. + +\section{Files} + +\begin{Description} +\item[\File{libunwind-coredump.h}] Header file to include when using the + interface defined by this library. +\item[\Opt{-l}\File{unwind-coredump} \Opt{-l}\File{unwind-generic}] + Linker-switches to add when building a program that uses the + functions defined by this library. +\end{Description} + +\section{See Also} + +\SeeAlso{libunwind}(3libunwind) + +\LatexManEnd +\end{document} diff --git a/src/native/external/libunwind/doc/libunwind-dynamic.man b/src/native/external/libunwind/doc/libunwind-dynamic.man index 68c66f3a329843..df28abaac8b664 100644 --- a/src/native/external/libunwind/doc/libunwind-dynamic.man +++ b/src/native/external/libunwind/doc/libunwind-dynamic.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,479 +12,478 @@ .fi .. -.TH "LIBUNWIND\-DYNAMIC" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "LIBUNWIND\-DYNAMIC" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME libunwind\-dynamic -\-\- libunwind\-support for runtime\-generated code +\-\- libunwind\-support for runtime\-generated code .PP .SH INTRODUCTION .PP For libunwind -to do its job, it needs to be able to reconstruct +to do its job, it needs to be able to reconstruct the \fIframe state\fP -of each frame in a call\-chain. The frame state -describes the subset of the machine\-state that consists of the +of each frame in a call\-chain. The frame state +describes the subset of the machine\-state that consists of the \fIframe registers\fP -(typically the instruction\-pointer and the -stack\-pointer) and all callee\-saved registers (preserved registers). -The frame state describes each register either by providing its -current value (for frame registers) or by providing the location at -which the current value is stored (callee\-saved registers). +(typically the instruction\-pointer and the +stack\-pointer) and all callee\-saved registers (preserved registers). +The frame state describes each register either by providing its +current value (for frame registers) or by providing the location at +which the current value is stored (callee\-saved registers). .PP -For statically generated code, the compiler normally takes care of +For statically generated code, the compiler normally takes care of emitting \fIunwind\-info\fP -which provides the minimum amount of -information needed to reconstruct the frame\-state for each instruction -in a procedure. For dynamically generated code, the runtime code -generator must use the dynamic unwind\-info interface provided by +which provides the minimum amount of +information needed to reconstruct the frame\-state for each instruction +in a procedure. For dynamically generated code, the runtime code +generator must use the dynamic unwind\-info interface provided by libunwind -to supply the equivalent information. This manual -page describes the format of this information in detail. +to supply the equivalent information. This manual +page describes the format of this information in detail. .PP For the purpose of this discussion, a \fIprocedure\fP -is defined to +is defined to be an arbitrary piece of \fIcontiguous\fP -code. Normally, each -procedure directly corresponds to a function in the source\-language -but this is not strictly required. For example, a runtime -code\-generator could translate a given function into two separate -(discontiguous) procedures: one for frequently\-executed (hot) code and -one for rarely\-executed (cold) code. Similarly, simple -source\-language functions (usually leaf functions) may get translated -into code for which the default unwind\-conventions apply and for such -code, it is not strictly necessary to register dynamic unwind\-info. +code. Normally, each +procedure directly corresponds to a function in the source\-language +but this is not strictly required. For example, a runtime +code\-generator could translate a given function into two separate +(discontiguous) procedures: one for frequently\-executed (hot) code and +one for rarely\-executed (cold) code. Similarly, simple +source\-language functions (usually leaf functions) may get translated +into code for which the default unwind\-conventions apply and for such +code, it is not strictly necessary to register dynamic unwind\-info. .PP A procedure logically consists of a sequence of \fIregions\fP\&. -Regions are nested in the sense that the frame state at the end of one -region is, by default, assumed to be the frame state for the next -region. Each region is thought of as being divided into a +Regions are nested in the sense that the frame state at the end of one +region is, by default, assumed to be the frame state for the next +region. Each region is thought of as being divided into a \fIprologue\fP, a \fIbody\fP, and an \fIepilogue\fP\&. -Each of them -can be empty. If non\-empty, the prologue sets up the frame state for -the body. For example, the prologue may need to allocate some space -on the stack and save certain callee\-saved registers. The body -performs the actual work of the procedure but does not change the -frame state in any way. If non\-empty, the epilogue restores the -previous frame state and as such it undoes or cancels the effect of -the prologue. In fact, a single epilogue may undo the effect of the -prologues of several (nested) regions. -.PP -We should point out that even though the prologue, body, and epilogue -are logically separate entities, optimizing code\-generators will -generally interleave instructions from all three entities. For this +Each of them +can be empty. If non\-empty, the prologue sets up the frame state for +the body. For example, the prologue may need to allocate some space +on the stack and save certain callee\-saved registers. The body +performs the actual work of the procedure but does not change the +frame state in any way. If non\-empty, the epilogue restores the +previous frame state and as such it undoes or cancels the effect of +the prologue. In fact, a single epilogue may undo the effect of the +prologues of several (nested) regions. +.PP +We should point out that even though the prologue, body, and epilogue +are logically separate entities, optimizing code\-generators will +generally interleave instructions from all three entities. For this reason, the dynamic unwind\-info interface of libunwind -makes no -distinction whatsoever between prologue and body. Similarly, the -exact set of instructions that make up an epilogue is also irrelevant. -The only point in the epilogue that needs to be described explicitly -by the dynamic unwind\-info is the point at which the stack\-pointer -gets restored. The reason this point needs to be described is that -once the stack\-pointer is restored, all values saved in the -deallocated portion of the stack frame become invalid and hence +makes no +distinction whatsoever between prologue and body. Similarly, the +exact set of instructions that make up an epilogue is also irrelevant. +The only point in the epilogue that needs to be described explicitly +by the dynamic unwind\-info is the point at which the stack\-pointer +gets restored. The reason this point needs to be described is that +once the stack\-pointer is restored, all values saved in the +deallocated portion of the stack frame become invalid and hence libunwind -needs to know about it. The portion of the frame -state not saved on the stack is assumed to remain valid through the end -of the region. For this reason, there is usually no need to describe -instructions which restore the contents of callee\-saved registers. -.PP -Within a region, each instruction that affects the frame state in some -fashion needs to be described with an operation descriptor. For this -purpose, each instruction in the region is assigned a unique index. -Exactly how this index is derived depends on the architecture. For -example, on RISC and EPIC\-style architecture, instructions have a -fixed size so it\&'s possible to simply number the instructions. In -contrast, most CISC use variable\-length instruction encodings, so it -is usually necessary to use a byte\-offset as the index. Given the -instruction index, the operation descriptor specifies the effect of -the instruction in an abstract manner. For example, it might express +needs to know about it. The portion of the frame +state not saved on the stack is assumed to remain valid through the end +of the region. For this reason, there is usually no need to describe +instructions which restore the contents of callee\-saved registers. +.PP +Within a region, each instruction that affects the frame state in some +fashion needs to be described with an operation descriptor. For this +purpose, each instruction in the region is assigned a unique index. +Exactly how this index is derived depends on the architecture. For +example, on RISC and EPIC\-style architecture, instructions have a +fixed size so it\&'s possible to simply number the instructions. In +contrast, most CISC use variable\-length instruction encodings, so it +is usually necessary to use a byte\-offset as the index. Given the +instruction index, the operation descriptor specifies the effect of +the instruction in an abstract manner. For example, it might express that the instruction stores callee\-saved register r1 -at offset 16 -in the stack frame. +at offset 16 +in the stack frame. .PP .SH PROCEDURES .PP -A runtime code\-generator registers the dynamic unwind\-info of a +A runtime code\-generator registers the dynamic unwind\-info of a procedure by setting up a structure of type unw_dyn_info_t and calling _U_dyn_register(), -passing the address of the -structure as the sole argument. The members of the +passing the address of the +structure as the sole argument. The members of the unw_dyn_info_t -structure are described below: +structure are described below: .TP void *next Private to libunwind\&. -Must not be used -by the application. +Must not be used +by the application. .TP void *prev Private to libunwind\&. -Must not be used -by the application. +Must not be used +by the application. .TP unw_word_t start_ip - The start\-address of the -instructions of the procedure (remember: procedure are defined to be -contiguous pieces of code, so a single code\-range is sufficient). + The start\-address of the +instructions of the procedure (remember: procedure are defined to be +contiguous pieces of code, so a single code\-range is sufficient). .TP unw_word_t end_ip - The end\-address of the -instructions of the procedure (non\-inclusive, that is, + The end\-address of the +instructions of the procedure (non\-inclusive, that is, end_ip\-start_ip -is the size of the procedure in -bytes). +is the size of the procedure in +bytes). .TP unw_word_t gp - The global\-pointer value in use -for this procedure. The exact meaning of the global\-pointer is -architecture\-specific and on some architecture, it is not used at -all. + The global\-pointer value in use +for this procedure. The exact meaning of the global\-pointer is +architecture\-specific and on some architecture, it is not used at +all. .TP int32_t format - The format of the unwind\-info. + The format of the unwind\-info. This member can be one of UNW_INFO_FORMAT_DYNAMIC, UNW_INFO_FORMAT_TABLE, -or +or UNW_INFO_FORMAT_REMOTE_TABLE\&. .TP union u - This union contains one sub\-member -structure for every possible unwind\-info format: + This union contains one sub\-member +structure for every possible unwind\-info format: .RS .TP unw_dyn_proc_info_t pi - This member is used + This member is used for format UNW_INFO_FORMAT_DYNAMIC\&. .TP unw_dyn_table_info_t ti - This member is used + This member is used for format UNW_INFO_FORMAT_TABLE\&. .TP unw_dyn_remote_table_info_t rti - This member + This member is used for format UNW_INFO_FORMAT_REMOTE_TABLE\&. .RE .RS .PP -The format of these sub\-members is described in detail below. +The format of these sub\-members is described in detail below. .RE .PP .SS PROC\-INFO FORMAT .PP -This is the preferred dynamic unwind\-info format and it is generally -the one used by full\-blown runtime code\-generators. In this format, -the details of a procedure are described by a structure of type +This is the preferred dynamic unwind\-info format and it is generally +the one used by full\-blown runtime code generators. In this format, +the details of a procedure are described by a structure of type unw_dyn_proc_info_t\&. -This structure contains the following -members: +This structure contains the following +members: .PP -.RE .TP unw_word_t name_ptr - The address of a -(human\-readable) name of the procedure or 0 if no such name is -available. If non\-zero, the string stored at this address must be -ASCII NUL terminated. For source languages that use name\-mangling -(such as C++ or Java) the string stored at this address should be + The address of a +(human\-readable) name of the procedure or 0 if no such name is +available. If non\-zero, the string stored at this address must be +ASCII NUL terminated. For source languages that use name mangling +(such as C++ or Java) the string stored at this address should be the \fIdemangled\fP -version of the name. +version of the name. .PP .TP unw_word_t handler - The address of the -personality\-routine for this procedure. Personality\-routines are -used in conjunction with exception handling. See the C++ ABI draft -(http://www.codesourcery.com/cxx\-abi/) for an overview and a -description of the personality routine. If the procedure has no + The address of the +personality routine for this procedure. Personality routines are +used in conjunction with exception handling. See the C++ ABI draft +(http://www.codesourcery.com/cxx\-abi/) for an overview and a +description of the personality routine. If the procedure has no personality routine, handler -must be set to 0. +must be set to 0. .PP .TP uint32_t flags - A bitmask of flags. At the -moment, no flags have been defined and this member must be -set to 0. + A bitmask of flags. At the +moment, no flags have been defined and this member must be +set to 0. .PP .TP unw_dyn_region_info_t *regions - A NULL\-terminated -linked list of region\-descriptors. See section ``Region -descriptors\&'' below for more details. + A NULL\-terminated +linked list of region descriptors. See section ``Region +descriptors\&'' below for more details. .PP .SS TABLE\-INFO FORMAT .PP -This format is generally used when the dynamically generated code was -derived from static code and the unwind\-info for the dynamic and the -static versions are identical. For example, this format can be useful -when loading statically\-generated code into an address\-space in a -non\-standard fashion (i.e., through some means other than +This format is generally used when the dynamically generated code was +derived from static code and the unwind\-info for the dynamic and the +static versions are identical. For example, this format can be useful +when loading statically\-generated code into an address\-space in a +non\-standard fashion (i.e., through some means other than dlopen()). -In this format, the details of a group of procedures +In this format, the details of a group of procedures is described by a structure of type unw_dyn_table_info\&. -This structure contains the following members: +This structure contains the following members: .PP .TP unw_word_t name_ptr - The address of a -(human\-readable) name of the procedure or 0 if no such name is -available. If non\-zero, the string stored at this address must be -ASCII NUL terminated. For source languages that use name\-mangling -(such as C++ or Java) the string stored at this address should be + The address of a +(human\-readable) name of the procedure or 0 if no such name is +available. If non\-zero, the string stored at this address must be +ASCII NUL terminated. For source languages that use name\-mangling +(such as C++ or Java) the string stored at this address should be the \fIdemangled\fP -version of the name. +version of the name. .PP .TP unw_word_t segbase - The segment\-base value -that needs to be added to the segment\-relative values stored in the -unwind\-info. The exact meaning of this value is -architecture\-specific. + The segment\-base value +that needs to be added to the segment\-relative values stored in the +unwind\-info. The exact meaning of this value is +architecture\-specific. .PP .TP unw_word_t table_len - The length of the + The length of the unwind\-info (table_data) -counted in units of words +counted in units of words (unw_word_t). .PP .TP unw_word_t table_data - A pointer to the actual -data encoding the unwind\-info. The exact format is -architecture\-specific (see architecture\-specific sections below). + A pointer to the actual +data encoding the unwind info. The exact format is +architecture\-specific (see architecture\-specific sections below). .PP .SS REMOTE TABLE\-INFO FORMAT .PP -The remote table\-info format has the same basic purpose as the regular +The remote table\-info format has the same basic purpose as the regular table\-info format. The only difference is that when libunwind -uses the unwind\-info, it will keep the table data in the target -address\-space (which may be remote). Consequently, the type of the +uses the unwind\-info, it will keep the table data in the target +address\-space (which may be remote). Consequently, the type of the table_data member is unw_word_t -rather than a pointer. +rather than a pointer. This implies that libunwind -will have to access the table\-data +will have to access the table\-data via the address\-space\&'s access_mem() -call\-back, rather than -through a direct memory reference. -.PP -From the point of view of a runtime\-code generator, the remote -table\-info format offers no advantage and it is expected that such -generators will describe their procedures either with the proc\-info -format or the normal table\-info format. The main reason that the -remote table\-info format exists is to enable the +call\-back, rather than +through a direct memory reference. +.PP +From the point of view of a runtime code generator, the remote +table\-info format offers no advantage and it is expected that such +generators will describe their procedures either with the proc\-info +format or the normal table\-info format. The main reason that the +remote table\-info format exists is to enable the address\-space\-specific find_proc_info() -callback (see -unw_create_addr_space(3)) -to return unwind tables whose -data remains in remote memory. This can speed up unwinding (e.g., for -a debugger) because it reduces the amount of data that needs to be -loaded from remote memory. +callback (see +unw_create_addr_space(3libunwind)) +to return unwind tables whose +data remains in remote memory. This can speed up unwinding (e.g., for +a debugger) because it reduces the amount of data that needs to be +loaded from remote memory. .PP .SH REGIONS DESCRIPTORS .PP -A region descriptor is a variable length structure that describes how -each instruction in the region affects the frame state. Of course, -most instructions in a region usually do not change the frame state and -for those, nothing needs to be recorded in the region descriptor. A -region descriptor is a structure of type +A region descriptor is a variable length structure that describes how +each instruction in the region affects the frame state. Of course, +most instructions in a region usually do not change the frame state and +for those, nothing needs to be recorded in the region descriptor. A +region descriptor is a structure of type unw_dyn_region_info_t -and has the following members: +and has the following members: .TP unw_dyn_region_info_t *next - A pointer to the + A pointer to the next region. If this is the last region, next is NULL\&. .TP int32_t insn_count - The length of the region in -instructions. Each instruction is assumed to have a fixed size (see -architecture\-specific sections for details). The value of + The length of the region in +instructions. Each instruction is assumed to have a fixed size (see +architecture\-specific sections for details). The value of insn_count -may be negative in the last region of a procedure +may be negative in the last region of a procedure (i.e., it may be negative only if next is NULL). -A +A negative value indicates that the region covers the last \fIN\fP instructions of the procedure, where \fIN\fP -is the absolute value +is the absolute value of insn_count\&. .TP uint32_t op_count - The (allocated) length of + The (allocated) length of the op_count -array. +array. .TP unw_dyn_op_t op - An array of dynamic unwind -directives. See Section ``Dynamic unwind directives\&'' for a -description of the directives. + An array of dynamic unwind +directives. See Section ``Dynamic unwind directives\&'' for a +description of the directives. .PP A region descriptor with an insn_count -of zero is an +of zero is an \fIempty region\fP -and such regions are perfectly legal. In fact, -empty regions can be useful to establish a particular frame state -before the start of another region. -.PP -A single region list can be shared across multiple procedures provided -those procedures share a common prologue and epilogue (their bodies -may differ, of course). Normally, such procedures consist of a canned -prologue, the body, and a canned epilogue. This could be described by -two regions: one covering the prologue and one covering the epilogue. -Since the body length is variable, the latter region would need to +and such regions are perfectly legal. In fact, +empty regions can be useful to establish a particular frame state +before the start of another region. +.PP +A single region list can be shared across multiple procedures provided +those procedures share a common prologue and epilogue (their bodies +may differ, of course). Normally, such procedures consist of a canned +prologue, the body, and a canned epilogue. This could be described by +two regions: one covering the prologue and one covering the epilogue. +Since the body length is variable, the latter region would need to specify a negative value in insn_count -such that +such that libunwind -knows that the region covers the end of the procedure +knows that the region covers the end of the procedure (up to the address specified by end_ip). .PP -The region descriptor is a variable length structure to make it -possible to allocate all the necessary memory with a single -memory\-allocation request. To facilitate the allocation of a region +The region descriptor is a variable length structure to make it +possible to allocate all the necessary memory with a single +memory\-allocation request. To facilitate the allocation of a region descriptors libunwind -provides a helper routine with the -following synopsis: +provides a helper routine with the +following synopsis: .PP size_t _U_dyn_region_size(int op_count); .PP -This routine returns the number of bytes needed to hold a region +This routine returns the number of bytes needed to hold a region descriptor with space for op_count -unwind directives. Note +unwind directives. Note that the length of the op -array does not have to match exactly -with the number of directives in a region. Instead, it is sufficient +array does not have to match exactly +with the number of directives in a region. Instead, it is sufficient if the op -array contains at least as many entries as there are -directives, since the end of the directives can always be indicated +array contains at least as many entries as there are +directives, since the end of the directives can always be indicated with the UNW_DYN_STOP -directive. +directive. .PP .SH DYNAMIC UNWIND DIRECTIVES .PP -A dynamic unwind directive describes how the frame state changes -at a particular point within a region. The description is in +A dynamic unwind directive describes how the frame state changes +at a particular point within a region. The description is in the form of a structure of type unw_dyn_op_t\&. -This -structure has the following members: +This +structure has the following members: .TP int8_t tag - The operation tag. Must be one + The operation tag. Must be one of the unw_dyn_operation_t -values described below. +values described below. .TP int8_t qp - The qualifying predicate that controls -whether or not this directive is active. This is useful for -predicated architectures such as IA\-64 or ARM, where the contents of -another (callee\-saved) register determines whether or not an -instruction is executed (takes effect). If the directive is always -active, this member should be set to the manifest constant + The qualifying predicate that controls +whether or not this directive is active. This is useful for +predicated architectures such as IA\-64 or ARM, where the contents of +another (callee\-saved) register determines whether or not an +instruction is executed (takes effect). If the directive is always +active, this member should be set to the manifest constant _U_QP_TRUE -(this constant is defined for all -architectures, predicated or not). +(this constant is defined for all +architectures, predicated or not). .TP int16_t reg - The number of the register affected -by the instruction. + The number of the register affected +by the instruction. .TP int32_t when - The region\-relative number of -the instruction to which this directive applies. For example, -a value of 0 means that the effect described by this directive -has taken place once the first instruction in the region has -executed. + The region\-relative number of +the instruction to which this directive applies. For example, +a value of 0 means that the effect described by this directive +has taken place once the first instruction in the region has +executed. .TP unw_word_t val - The value to be applied by the -operation tag. The exact meaning of this value varies by tag. See -Section ``Operation tags\&'' below. + The value to be applied by the +operation tag. The exact meaning of this value varies by tag. See +Section ``Operation tags\&'' below. .PP -It is perfectly legitimate to specify multiple dynamic unwind +It is perfectly legitimate to specify multiple dynamic unwind directives with the same when -value, if a particular instruction -has a complex effect on the frame state. +value, if a particular instruction +has a complex effect on the frame state. .PP -Empty regions by definition contain no actual instructions and as such -the directives are not tied to a particular instruction. By +Empty regions by definition contain no actual instructions and as such +the directives are not tied to a particular instruction. By convention, the when -member should be set to 0, however. +member should be set to 0, however. .PP -There is no need for the dynamic unwind directives to appear +There is no need for the dynamic unwind directives to appear in order of increasing when -values. If the directives happen to -be sorted in that order, it may result in slightly faster execution, -but a runtime code\-generator should not go to extra lengths just to -ensure that the directives are sorted. +values. If the directives happen to +be sorted in that order, it may result in slightly faster execution, +but a runtime code\-generator should not go to extra lengths just to +ensure that the directives are sorted. .PP IMPLEMENTATION NOTE: should libunwind -implementations for -certain architectures prefer the list of unwind directives to be -sorted, it is recommended that such implementations first check -whether the list happens to be sorted already and, if not, sort the -directives explicitly before the first use. With this approach, the -overhead of explicit sorting is only paid when there is a real benefit -and if the runtime code\-generator happens to generate sorted lists -naturally, the performance penalty is limited to a simple O(N) check. +implementations for +certain architectures prefer the list of unwind directives to be +sorted, it is recommended that such implementations first check +whether the list happens to be sorted already and, if not, sort the +directives explicitly before the first use. With this approach, the +overhead of explicit sorting is only paid when there is a real benefit +and if the runtime code\-generator happens to generate sorted lists +naturally, the performance penalty is limited to a simple O(N) check. .PP .SS OPERATIONS TAGS .PP -The possible operation tags are defined by enumeration type +The possible operation tags are defined by enumeration type unw_dyn_operation_t -which defines the following -values: +which defines the following +values: .PP .TP UNW_DYN_STOP - Marks the end of the dynamic unwind + Marks the end of the dynamic unwind directive list. All remaining entries in the op -array of the -region\-descriptor are ignored. This tag is guaranteed to have a -value of 0. +array of the +region\-descriptor are ignored. This tag is guaranteed to have a +value of 0. .PP .TP UNW_DYN_SAVE_REG - Marks an instruction which saves + Marks an instruction which saves register reg to register val\&. .PP .TP UNW_DYN_SPILL_FP_REL - Marks an instruction which + Marks an instruction which spills register reg -to a frame\-pointer\-relative location. The -frame\-pointer\-relative offset is given by the value stored in member +to a frame\-pointer\-relative location. The +frame\-pointer\-relative offset is given by the value stored in member val\&. -See the architecture\-specific sections for a description -of the stack frame layout. +See the architecture\-specific sections for a description +of the stack frame layout. .PP .TP UNW_DYN_SPILL_SP_REL - Marks an instruction which + Marks an instruction which spills register reg -to a stack\-pointer\-relative location. The -stack\-pointer\-relative offset is given by the value stored in member +to a stack\-pointer\-relative location. The +stack\-pointer\-relative offset is given by the value stored in member val\&. -See the architecture\-specific sections for a description -of the stack frame layout. +See the architecture\-specific sections for a description +of the stack frame layout. .PP .TP UNW_DYN_ADD - Marks an instruction which adds + Marks an instruction which adds the constant value val to register reg\&. -To add subtract -a constant value, store the two\&'s\-complement of the value in +To add subtract +a constant value, store the two\&'s\-complement of the value in val\&. -The set of registers that can be specified for this tag -is described in the architecture\-specific sections below. +The set of registers that can be specified for this tag +is described in the architecture\-specific sections below. .PP .TP UNW_DYN_POP_FRAMES @@ -496,36 +497,36 @@ UNW_DYN_COPY_STATE .TP UNW_DYN_ALIAS .PP -unw_dyn_op_t -.PP -_U_dyn_op_save_reg(); -_U_dyn_op_spill_fp_rel(); -_U_dyn_op_spill_sp_rel(); -_U_dyn_op_add(); -_U_dyn_op_pop_frames(); -_U_dyn_op_label_state(); -_U_dyn_op_copy_state(); -_U_dyn_op_alias(); -_U_dyn_op_stop(); +unw_dyn_op_t +.PP +_U_dyn_op_save_reg(); +_U_dyn_op_spill_fp_rel(); +_U_dyn_op_spill_sp_rel(); +_U_dyn_op_add(); +_U_dyn_op_pop_frames(); +_U_dyn_op_label_state(); +_U_dyn_op_copy_state(); +_U_dyn_op_alias(); +_U_dyn_op_stop(); .PP .SH IA\-64 SPECIFICS .PP -\- meaning of segbase member in table\-info/table\-remote\-info format -\- format of table_data in table\-info/table\-remote\-info format -\- instruction size: each bundle is counted as 3 instructions, regardless -of template (MLX) -\- describe stack\-frame layout, especially with regards to sp\-relative -and fp\-relative addressing -\- UNW_DYN_ADD can only add to ``sp\&'' (always a negative value); use -POP_FRAMES otherwise +\- meaning of segbase member in table\-info/table\-remote\-info format +\- format of table_data in table\-info/table\-remote\-info format +\- instruction size: each bundle is counted as 3 instructions, regardless +of template (MLX) +\- describe stack\-frame layout, especially with regards to sp\-relative +and fp\-relative addressing +\- UNW_DYN_ADD can only add to ``sp\&'' (always a negative value); use +POP_FRAMES otherwise .PP .SH SEE ALSO .PP -libunwind(3), -_U_dyn_register(3), -_U_dyn_cancel(3) +libunwind(3libunwind), +_U_dyn_register(3libunwind), +_U_dyn_cancel(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/libunwind-dynamic.tex b/src/native/external/libunwind/doc/libunwind-dynamic.tex index a3b7762b247157..85af41f5007f95 100644 --- a/src/native/external/libunwind/doc/libunwind-dynamic.tex +++ b/src/native/external/libunwind/doc/libunwind-dynamic.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{libunwind-dynamic}{David Mosberger-Tang}{Programming Library}{Introduction to dynamic unwind-info}libunwind-dynamic -- libunwind-support for runtime-generated code +\begin{Name}{3libunwind}{libunwind-dynamic}{David Mosberger-Tang}{Programming Library}{Introduction to dynamic unwind-info}libunwind-dynamic -- libunwind-support for runtime-generated code \end{Name} \section{Introduction} @@ -124,7 +124,7 @@ \section{Procedures} \subsection{Proc-info format} This is the preferred dynamic unwind-info format and it is generally -the one used by full-blown runtime code-generators. In this format, +the one used by full-blown runtime code generators. In this format, the details of a procedure are described by a structure of type \Type{unw\_dyn\_proc\_info\_t}. This structure contains the following members: @@ -133,12 +133,12 @@ \subsection{Proc-info format} \item[\Type{unw\_word\_t} \Var{name\_ptr}] The address of a (human-readable) name of the procedure or 0 if no such name is available. If non-zero, the string stored at this address must be - ASCII NUL terminated. For source languages that use name-mangling + ASCII NUL terminated. For source languages that use name mangling (such as C++ or Java) the string stored at this address should be the \emph{demangled} version of the name. \item[\Type{unw\_word\_t} \Var{handler}] The address of the - personality-routine for this procedure. Personality-routines are + personality routine for this procedure. Personality routines are used in conjunction with exception handling. See the C++ ABI draft (http://www.codesourcery.com/cxx-abi/) for an overview and a description of the personality routine. If the procedure has no @@ -149,7 +149,7 @@ \subsection{Proc-info format} set to 0. \item[\Type{unw\_dyn\_region\_info\_t~*}\Var{regions}] A NULL-terminated - linked list of region-descriptors. See section ``Region + linked list of region descriptors. See section ``Region descriptors'' below for more details. \end{description} @@ -183,7 +183,7 @@ \subsection{Table-info format} (\Type{unw\_word\_t}). \item[\Type{unw\_word\_t} \Var{table\_data}] A pointer to the actual - data encoding the unwind-info. The exact format is + data encoding the unwind info. The exact format is architecture-specific (see architecture-specific sections below). \end{description} @@ -199,13 +199,13 @@ \subsection{Remote table-info format} via the address-space's \Func{access\_mem}() call-back, rather than through a direct memory reference. -From the point of view of a runtime-code generator, the remote +From the point of view of a runtime code generator, the remote table-info format offers no advantage and it is expected that such generators will describe their procedures either with the proc-info format or the normal table-info format. The main reason that the remote table-info format exists is to enable the address-space-specific \Func{find\_proc\_info}() callback (see -\SeeAlso{unw\_create\_addr\_space}(3)) to return unwind tables whose +\SeeAlso{unw\_create\_addr\_space}(3libunwind)) to return unwind tables whose data remains in remote memory. This can speed up unwinding (e.g., for a debugger) because it reduces the amount of data that needs to be loaded from remote memory. @@ -386,9 +386,9 @@ \section{IA-64 specifics} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{\_U\_dyn\_register(3)}, -\SeeAlso{\_U\_dyn\_cancel(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{\_U\_dyn\_register}(3libunwind), +\SeeAlso{\_U\_dyn\_cancel}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/libunwind-ia64.man b/src/native/external/libunwind/doc/libunwind-ia64.man index 06b141eb3e2b70..6441e4dd5e420f 100644 --- a/src/native/external/libunwind/doc/libunwind-ia64.man +++ b/src/native/external/libunwind/doc/libunwind-ia64.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:44 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "LIBUNWIND\-IA64" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "LIBUNWIND\-IA64" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME libunwind\-ia64 \-\- IA\-64\-specific support in libunwind @@ -301,7 +303,7 @@ portable code should not rely on this equivalence. .SH SEE ALSO .PP -libunwind(3) +libunwind(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/libunwind-ia64.tex b/src/native/external/libunwind/doc/libunwind-ia64.tex index c08946dc4b327d..76a688a980ad9b 100644 --- a/src/native/external/libunwind/doc/libunwind-ia64.tex +++ b/src/native/external/libunwind/doc/libunwind-ia64.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{libunwind-ia64}{David Mosberger-Tang}{Programming Library}{IA-64-specific support in libunwind}libunwind-ia64 -- IA-64-specific support in libunwind +\begin{Name}{3libunwind}{libunwind-ia64}{David Mosberger-Tang}{Programming Library}{IA-64-specific support in libunwind}libunwind-ia64 -- IA-64-specific support in libunwind \end{Name} @@ -203,7 +203,7 @@ \section{The Unwind-Context Type} \section{See Also} -\SeeAlso{libunwind(3)} +\SeeAlso{libunwind}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/libunwind-nto.man b/src/native/external/libunwind/doc/libunwind-nto.man new file mode 100644 index 00000000000000..8b105cfaf90735 --- /dev/null +++ b/src/native/external/libunwind/doc/libunwind-nto.man @@ -0,0 +1,256 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} +'\" t +.\" Manual page created with latex2man on Tue Aug 29 10:53:41 2023 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "LIBUNWIND\-NTO" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " +.SH NAME +libunwind\-nto +\-\- QNX Neutrino support in libunwind +.SH SYNOPSIS + +.PP +#include +.br +.PP +unw_accessors_t +unw_nto_accessors; +.br +.PP +void *unw_nto_create(pid_t, +pthread_t); +.br +void unw_nto_destroy(void *); +.br +.PP +int +unw_nto_find_proc_info(unw_addr_space_t, +unw_word_t, +unw_proc_info_t *, +int, +void *); +.br +void +unw_nto_put_unwind_info(unw_addr_space_t, +unw_proc_info_t *, +void *); +.br +int +unw_nto_get_dyn_info_list_addr(unw_addr_space_t, +unw_word_t *, +void *); +.br +int +unw_nto_access_mem(unw_addr_space_t, +unw_word_t, +unw_word_t *, +int, +void *); +.br +int +unw_nto_access_reg(unw_addr_space_t, +unw_regnum_t, +unw_word_t *, +int, +void *); +.br +int +unw_nto_access_fpreg(unw_addr_space_t, +unw_regnum_t, +unw_fpreg_t *, +int, +void *); +.br +int +unw_nto_get_proc_name(unw_addr_space_t, +unw_word_t, +char *, +size_t, +unw_word_t *, +void *); +.br +int +unw_nto_resume(unw_addr_space_t, +unw_cursor_t *, +void *); +.br +.PP +.SH DESCRIPTION + +.PP +The QNX operating system makes it possible for a process to +gain access to the machine state and virtual memory of \fIanother\fP +process, or a different thread within the same process. +gain access to the machine state and virtual memory of \fIanother\fP +it is possible to hook up libunwind +to another process. +While it\&'s not very difficult to do so directly, +libunwind +further facilitates this task by providing +ready\-to\-use callbacks for this purpose. +The routines and variables +implementing this facility use a name prefix of unw_nto, +which is stands for ``unwind\-via\-nto\&''\&. +.PP +An application that wants to use the libunwind +NTO remote needs +to take the following steps. +.PP +.TP +1. +Create a new libunwind address\-space that represents the target +process and thread. This is done by calling +unw_create_addr_space(). +In many cases, the application will +simply want to pass the address of unw_nto_accessors +as the +first argument to this routine. Doing so will ensure that +libunwind +will be able to properly unwind the target process. +.PP +.TP +2. +Create an NTO info structure by calling unw_nto_create(), +passing the pid and tid of the target process thread as the arguments. +This will stop the target thread. The process ID (pid) of the target +process must be known, and only a single thread can be unwound at a time +so the thread ID (tid) must also be specified. +.PP +.TP +3. +The opaque pointer returned then needs to be passed as the +``argument\&'' pointer (third argument) to unw_init_remote(). +.PP +When the application is done using libunwind +on the target process, +unw_nto_destroy() +needs to be called, passing it the opaque pointer +that was returned by the call to unw_nto_create(). +This ensures that +all memory and other resources are freed up. +.PP +The unw_nto_resume() +is not supported on the NTO remote. +.PP +In special circumstances, an application may prefer to use +only portions of the libunwind +NTO remote. For this reason, the +individual callback routines (unw_nto_find_proc_info(), +unw_nto_put_unwind_info(), +etc.) are also available for direct +use. Of course, the addresses of these routines could also be picked +up from unw_nto_accessors, +but doing so would prevent static +initialization. Also, when using unw_nto_accessors, +\fIall\fP +the callback routines will be linked into the application, even if +they are never actually called. +.PP +.SH THREAD SAFETY + +.PP +The libunwind +NTO remote assumes that a single unw_nto\-info +structure is never shared between threads of the unwinding program. +Because of this, +no explicit locking is used. +As long as only one thread uses an NTO info structure at any given time, +this facility is thread\-safe. +.PP +.SH RETURN VALUE + +.PP +unw_nto_create() +may return a NULL if it fails +to create the NTO info structure for any reason. +.PP +.SH FILES + +.PP +.TP +libunwind\-nto.h + Headerfile to include when using the +interface defined by this library. +.TP +\fB\-l\fPunwind\-nto \fB\-l\fPunwind\-generic + Linker\-switches to add when building a program that uses the +functions defined by this library. +.PP +.SH EXAMPLE + +.Vb + #include + #include + #include + + int + main (int argc, char **argv) + { + if (argc != 2) { + fprintf (stderr, "usage: %s PID\\n", argv[0]); + exit (EXIT_FAILURE); + } + + char *endptr; + pid_t target_pid = strtoul (argv[1], &endptr, 10); + if (target_pid == 0 && argv[1] == endptr) { + fprintf (stderr, "usage: %s PID\\n", argv[0]); + exit (EXIT_FAILURE); + } + + unw_addr_space_t as = unw_create_addr_space (&unw_nto_accessors, 0); + if (!as) { + fprintf (stderr, "unw_create_addr_space() failed"); + exit (EXIT_FAILURE); + } + + void *ui = unw_nto_create (target_pid, (thread_t)1); + if (!ui) { + fprintf (stderr, "unw_nto_create() failed"); + exit (EXIT_FAILURE); + } + + unw_cursor_t cursor; + int ret = unw_init_remote (&cursor, as, ui); + if (ret < 0) { + fprintf (stderr, "unw_init_remote() failed: ret=%d\\n", ret); + exit (EXIT_FAILURE); + } + + do { + unw_proc_info_t pi; + ret = unw_get_proc_info (&cursor, &pi); + if (ret == \-UNW_ENOINFO) { + fprintf (stdout, "no info\\n"); + } else if (ret >= 0) { + printf ("\\tproc=%#016lx\-%#016lx\\thandler=%#016lx lsda=%#016lx", + (long) pi.start_ip, (long) pi.end_ip, + (long) pi.handler, (long) pi.lsda); + } + ret = unw_step (&cursor); + } while (ret > 0); + if (ret < 0) { + fprintf (stderr, "unwind failed with ret=%d\\n", ret); + exit (EXIT_FAILURE); + } + + unw_nto_destroy (ui); + unw_destroy_addr_space (as); + exit (EXIT_SUCCESS); + } +.Ve +.PP +.SH SEE ALSO + +libunwind(3libunwind) +.PP +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/src/native/external/libunwind/doc/libunwind-nto.tex b/src/native/external/libunwind/doc/libunwind-nto.tex new file mode 100644 index 00000000000000..74c59912cb7d9a --- /dev/null +++ b/src/native/external/libunwind/doc/libunwind-nto.tex @@ -0,0 +1,183 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3libunwind}{libunwind-nto}{Blackberry}{Programming Library}{QNX Neutrino remote for libunwind}libunwind-nto -- QNX Neutrino support in libunwind +\end{Name} +\section{Synopsis} + +\File{\#include $<$libunwind-nto.h$>$}\\ + +\noindent +\Type{unw\_accessors\_t} \Var{unw\_nto\_accessors};\\ + +\Type{void~*}\Func{unw\_nto\_create}(\Type{pid\_t}, \Type{pthread\_t});\\ +\noindent +\Type{void}~\Func{unw\_nto\_destroy}(\Type{void~*});\\ + +\noindent +\Type{int} \Func{unw\_nto\_find\_proc\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_proc\_info\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{void} \Func{unw\_nto\_put\_unwind\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_proc\_info\_t~*}, \Type{void~*});\\ +\noindent +\Type{int} \Func{unw\_nto\_get\_dyn\_info\_list\_addr}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ +\noindent +\Type{int} \Func{unw\_nto\_access\_mem}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{int} \Func{unw\_nto\_access\_reg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{int} \Func{unw\_nto\_access\_fpreg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_fpreg\_t~*}, \Type{int}, \Type{void~*});\\ +\noindent +\Type{int} \Func{unw\_nto\_get\_proc\_name}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{char~*}, \Type{size\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ +\noindent +\Type{int} \Func{unw\_nto\_resume}(\Type{unw\_addr\_space\_t}, \Type{unw\_cursor\_t~*}, \Type{void~*});\\ + +\section{Description} + +The QNX operating system makes it possible for a process to +gain access to the machine state and virtual memory of \emph{another} +process, or a different thread within the same process. +gain access to the machine state and virtual memory of \emph{another} +it is possible to hook up \Prog{libunwind} to another process. +While it's not very difficult to do so directly, +\Prog{libunwind} further facilitates this task by providing +ready-to-use callbacks for this purpose. +The routines and variables +implementing this facility use a name prefix of \Func{unw\_nto}, +which is stands for ``unwind-via-nto''. + +An application that wants to use the \Prog{libunwind} NTO remote needs +to take the following steps. +\begin{enumerate} + + \item Create a new \Prog{libunwind} address-space that represents the target + process and thread. This is done by calling + \Func{unw\_create\_addr\_space}(). In many cases, the application will + simply want to pass the address of \Var{unw\_nto\_accessors} as the + first argument to this routine. Doing so will ensure that + \Prog{libunwind} will be able to properly unwind the target process. + + \item Create an NTO info structure by calling \Func{unw\_nto\_create}(), + passing the pid and tid of the target process thread as the arguments. + This will stop the target thread. The process ID (pid) of the target + process must be known, and only a single thread can be unwound at a time + so the thread ID (tid) must also be specified. + + \item The opaque pointer returned then needs to be passed as the + ``argument'' pointer (third argument) to \Func{unw\_init\_remote}(). + +\end{enumerate} + +When the application is done using \Prog{libunwind} on the target process, +\Func{unw\_nto\_destroy}() needs to be called, passing it the opaque pointer +that was returned by the call to \Func{unw\_nto\_create}(). This ensures that +all memory and other resources are freed up. + +The \Func{unw\_nto\_resume}() is not supported on the NTO remote. + +In special circumstances, an application may prefer to use +only portions of the \Prog{libunwind} NTO remote. For this reason, the +individual callback routines (\Func{unw\_nto\_find\_proc\_info}(), +\Func{unw\_nto\_put\_unwind\_info}(), etc.) are also available for direct +use. Of course, the addresses of these routines could also be picked +up from \Var{unw\_nto\_accessors}, but doing so would prevent static +initialization. Also, when using \Var{unw\_nto\_accessors}, \emph{all} +the callback routines will be linked into the application, even if +they are never actually called. + +\section{Thread Safety} + +The \Prog{libunwind} NTO remote assumes that a single \Prog{unw\_nto}-info +structure is never shared between threads of the unwinding program. +Because of this, +no explicit locking is used. +As long as only one thread uses an NTO info structure at any given time, +this facility is thread-safe. + +\section{Return Value} + +\Func{unw\_nto\_create}() may return a NULL if it fails +to create the NTO info structure for any reason. + +\section{Files} + +\begin{Description} +\item[\File{libunwind-nto.h}] Headerfile to include when using the + interface defined by this library. +\item[\Opt{-l}\File{unwind-nto} \Opt{-l}\File{unwind-generic}] + Linker-switches to add when building a program that uses the + functions defined by this library. +\end{Description} + +\section{Example} +\begin{verbatim} + #include + #include + #include + + int + main (int argc, char **argv) + { + if (argc != 2) { + fprintf (stderr, "usage: %s PID\n", argv[0]); + exit (EXIT_FAILURE); + } + + char *endptr; + pid_t target_pid = strtoul (argv[1], &endptr, 10); + if (target_pid == 0 && argv[1] == endptr) { + fprintf (stderr, "usage: %s PID\n", argv[0]); + exit (EXIT_FAILURE); + } + + unw_addr_space_t as = unw_create_addr_space (&unw_nto_accessors, 0); + if (!as) { + fprintf (stderr, "unw_create_addr_space() failed"); + exit (EXIT_FAILURE); + } + + void *ui = unw_nto_create (target_pid, (thread_t)1); + if (!ui) { + fprintf (stderr, "unw_nto_create() failed"); + exit (EXIT_FAILURE); + } + + unw_cursor_t cursor; + int ret = unw_init_remote (&cursor, as, ui); + if (ret < 0) { + fprintf (stderr, "unw_init_remote() failed: ret=%d\n", ret); + exit (EXIT_FAILURE); + } + + do { + unw_proc_info_t pi; + ret = unw_get_proc_info (&cursor, &pi); + if (ret == -UNW_ENOINFO) { + fprintf (stdout, "no info\n"); + } else if (ret >= 0) { + printf ("\tproc=%#016lx-%#016lx\thandler=%#016lx lsda=%#016lx", + (long) pi.start_ip, (long) pi.end_ip, + (long) pi.handler, (long) pi.lsda); + } + ret = unw_step (&cursor); + } while (ret > 0); + if (ret < 0) { + fprintf (stderr, "unwind failed with ret=%d\n", ret); + exit (EXIT_FAILURE); + } + + unw_nto_destroy (ui); + unw_destroy_addr_space (as); + exit (EXIT_SUCCESS); + } +\end{verbatim} + +\section{See Also} +\SeeAlso{libunwind}(3libunwind) + +\LatexManEnd + +\end{document} diff --git a/src/native/external/libunwind/doc/libunwind-ptrace.man b/src/native/external/libunwind/doc/libunwind-ptrace.man index 985fcae275333c..415d972206a439 100644 --- a/src/native/external/libunwind/doc/libunwind-ptrace.man +++ b/src/native/external/libunwind/doc/libunwind-ptrace.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:44 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 10:53:41 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,10 +12,10 @@ .fi .. -.TH "LIBUNWIND\-PTRACE" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "LIBUNWIND\-PTRACE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME libunwind\-ptrace -\-\- ptrace() support in libunwind +\-\- ptrace() support in libunwind .PP .SH SYNOPSIS @@ -27,58 +29,49 @@ _UPT_accessors; .PP void *_UPT_create(pid_t); .br -void -_UPT_destroy(void *); +void _UPT_destroy(void *); .br .PP -int -_UPT_find_proc_info(unw_addr_space_t, +int _UPT_find_proc_info(unw_addr_space_t, unw_word_t, unw_proc_info_t *, int, void *); .br -void -_UPT_put_unwind_info(unw_addr_space_t, +void _UPT_put_unwind_info(unw_addr_space_t, unw_proc_info_t *, void *); .br -int -_UPT_get_dyn_info_list_addr(unw_addr_space_t, +int _UPT_get_dyn_info_list_addr(unw_addr_space_t, unw_word_t *, void *); .br -int -_UPT_access_mem(unw_addr_space_t, +int _UPT_access_mem(unw_addr_space_t, unw_word_t, unw_word_t *, int, void *); .br -int -_UPT_access_reg(unw_addr_space_t, +int _UPT_access_reg(unw_addr_space_t, unw_regnum_t, unw_word_t *, int, void *); .br -int -_UPT_access_fpreg(unw_addr_space_t, +int _UPT_access_fpreg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, void *); .br -int -_UPT_get_proc_name(unw_addr_space_t, +int _UPT_get_proc_name(unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); .br -int -_UPT_resume(unw_addr_space_t, +int _UPT_resume(unw_addr_space_t, unw_cursor_t *, void *); .br @@ -87,9 +80,9 @@ void *); .PP The ptrace(2) -system\-call makes it possible for a process to -gain access to the machine\-state and virtual memory of \fIanother\fP -process. With the right set of call\-back routines, it is therefore +system call makes it possible for a process to +gain access to the machine state and virtual memory of \fIanother\fP +process. With the right set of callback routines, it is therefore possible to hook up libunwind to another process via ptrace(2). @@ -97,117 +90,185 @@ While it\&'s not very difficult to do so directly, libunwind further facilitates this task by providing ready\-to\-use callbacks for this purpose. The routines and variables -implementing this facility use a name\-prefix of _UPT, +implementing this facility use a name prefix of _UPT, which is -stands for ``unwind\-via\-ptrace\&''\&. -.PP -An application that wants to use the _UPT\-facility -first needs -to create a new libunwind -address\-space that represents the -target process. This is done by calling -unw_create_addr_space(). -In many cases, the application -will simply want to pass the address of _UPT_accessors -as the -first argument to this routine. Doing so will ensure that -libunwind -will be able to properly unwind the target process. -However, in special circumstances, an application may prefer to use -only portions of the _UPT\-facility. -For this reason, the -individual callback routines (_UPT_find_proc_info(), +stands for ``unwind via ptrace\&''\&. +.PP +An application that wants to use the libunwind +ptrace remote needs to +take the folowing steps. +.PP +.TP +1. +Create a new libunwind address space that represents the target +process. This is done by calling unw_create_addr_space(). +In +many cases, the application will simply want to pass the address of +_UPT_accessors +as the first argument to this routine. Doing so +will ensure that libunwind +will be able to properly unwind the +target process. +.PP +.TP +2. +Turn on ptrace mode on the target process, either by forking a new +process, invoking PTRACE_TRACEME, +and then starting the target +program (via execve(2)), +or by directly attaching to an already +running process (via PTRACE_ATTACH). +.PP +.TP +3. +Once the process\-ID (pid) of the target process is known, a +UPT info structure can be created by calling +_UPT_create(), +passing the pid of the target process as the +only argument. +.PP +.TP +4. +The opaque pointer returned then needs to be passed as the +``argument\&'' pointer (third argument) to unw_init_remote(). +.PP +In special circumstances, an application may prefer to use only +portions of the libunwind +ptrace remote. For this reason, the individual +callback routines (_UPT_find_proc_info(), _UPT_put_unwind_info(), -etc.) are also available for direct -use. Of course, the addresses of these routines could also be picked -up from _UPT_accessors, -but doing so would prevent static -initialization. Also, when using _UPT_accessors, +etc.) are also available for direct use. Of +course, the addresses of these routines could also be picked up from +_UPT_accessors, +but doing so would prevent static initialization. Also, +when using _UPT_accessors, \fIall\fP -the callback routines will be linked into the application, even if -they are never actually called. -.PP -Next, the application can turn on ptrace\-mode on the target process, -either by forking a new process, invoking PTRACE_TRACEME, -and -then starting the target program (via execve(2)), -or by -directly attaching to an already running process (via -PTRACE_ATTACH). -Either way, once the process\-ID (pid) of the -target process is known, a _UPT\-info\-structure -can be created -by calling _UPT_create(), -passing the pid of the target process -as the only argument. The returned void\-pointer then needs to be -passed as the ``argument\&'' pointer (third argument) to -unw_init_remote(). +the callback routines will be +linked into the application, even if they are never actually called. .PP The _UPT_resume() -routine can be used to resume execution of -the target process. It simply invokes ptrace(2) -with a command -value of PTRACE_CONT\&. +routine can be used to resume execution of the target +process. It simply invokes ptrace(2) +with a command value of +PTRACE_CONT\&. .PP When the application is done using libunwind -on the target -process, _UPT_destroy() -needs to be called, passing it the -void\-pointer that was returned by the corresponding call to -_UPT_create(). -This ensures that all memory and other -resources are freed up. +on the target process, +_UPT_destroy() +needs to be called, passing it the opaque pointer that +was returned by the call to _UPT_create(). +This ensures that all +memory and other resources are freed up. .PP .SH AVAILABILITY .PP Since ptrace(2) -works within a single machine only, the -_UPT\-facility -by definition is not available in -libunwind\-versions -configured for cross\-unwinding. +works within a single machine only, the libunwind ptrace +remote by definition is not available in libunwind +versions configured +for cross\-unwinding. .PP .SH THREAD SAFETY .PP -The _UPT\-facility -assumes that a single _UPT\-info -structure is never shared between threads. Because of this, no -explicit locking is used. As long as only one thread uses -a _UPT\-info -structure at any given time, this facility -is thread\-safe. +The libunwind +ptrace remote assumes that a single UPT info structure is +never shared between threads. Because of this, no explicit locking is used. As +long as only one thread uses a UPT info structure at any given time, this +facility is thread\-safe. .PP .SH RETURN VALUE .PP _UPT_create() may return a NULL -pointer if it fails -to create the _UPT\-info\-structure -for any reason. For the -current implementation, the only reason this call may fail is when the -system is out of memory. +pointer if it fails to create +the UPT info structure for any reason. For the current implementation, the only +reason this call may fail is when the system is out of memory. .PP .SH FILES .PP .TP libunwind\-ptrace.h - Headerfile to include when using the + Header file to include when using the interface defined by this library. .TP \fB\-l\fPunwind\-ptrace \fB\-l\fPunwind\-generic - Linker\-switches to add when building a program that uses the + Linker switches to add when building a program that uses the functions defined by this library. .PP +.SH EXAMPLE + +.Vb + #include + #include + #include + + int + main (int argc, char **argv) + { + if (argc != 2) { + fprintf (stderr, "usage: %s PID\\n", argv[0]); + exit (EXIT_FAILURE); + } + + char *endptr; + pid_t target_pid = strtoul (argv[1], &endptr, 10); + if (target_pid == 0 && argv[1] == endptr) { + fprintf (stderr, "usage: %s PID\\n", argv[0]); + exit (EXIT_FAILURE); + } + + unw_addr_space_t as = unw_create_addr_space (&_UPT_accessors, 0); + if (!as) { + fprintf (stderr, "unw_create_addr_space() failed"); + exit (EXIT_FAILURE); + } + + void *ui = _UPT_create (target_pid); + if (!ui) { + fprintf (stderr, "_UPT_create() failed"); + exit (EXIT_FAILURE); + } + + unw_cursor_t cursor; + int ret = unw_init_remote (&cursor, as, ui); + if (ret < 0) { + fprintf (stderr, "unw_init_remote() failed: ret=%d\\n", ret); + exit (EXIT_FAILURE); + } + + do { + unw_proc_info_t pi; + ret = unw_get_proc_info (&cursor, &pi); + if (ret == \-UNW_ENOINFO) { + fprintf (stdout, "no info\\n"); + } else if (ret >= 0) { + printf ("\\tproc=%#016lx\-%#016lx\\thandler=%#016lx lsda=%#016lx", + (long) pi.start_ip, (long) pi.end_ip, + (long) pi.handler, (long) pi.lsda); + } + ret = unw_step (&cursor); + } while (ret > 0); + if (ret < 0) { + fprintf (stderr, "unwind failed with ret=%d\\n", ret); + exit (EXIT_FAILURE); + } + + _UPT_destroy (ui); + unw_destroy_addr_space (as); + exit (EXIT_SUCCESS); + } +.Ve +.PP .SH SEE ALSO .PP -execve(2), -libunwind(3), -ptrace(2) +execve(2), +libunwind(3libunwind), +ptrace(2) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/libunwind-ptrace.tex b/src/native/external/libunwind/doc/libunwind-ptrace.tex index fe074d8619174a..513314aa6acb46 100644 --- a/src/native/external/libunwind/doc/libunwind-ptrace.tex +++ b/src/native/external/libunwind/doc/libunwind-ptrace.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{libunwind-ptrace}{David Mosberger-Tang}{Programming Library}{ptrace() support in libunwind}libunwind-ptrace -- ptrace() support in libunwind +\begin{Name}{3libunwind}{libunwind-ptrace}{David Mosberger-Tang}{Programming Library}{ptrace() support in \Prog{libunwind}libunwind-ptrace -- ptrace() support in \Prog{libunwind} \end{Name} \section{Synopsis} @@ -17,111 +17,178 @@ \section{Synopsis} \Type{void~*}\Func{\_UPT\_create}(\Type{pid\_t});\\ \noindent -\Type{void} \Func{\_UPT\_destroy}(\Type{void~*});\\ +\Type{void}~\Func{\_UPT\_destroy}(\Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_find\_proc\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_proc\_info\_t~*}, \Type{int}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_find\_proc\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_proc\_info\_t~*}, \Type{int}, \Type{void~*});\\ \noindent -\Type{void} \Func{\_UPT\_put\_unwind\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_proc\_info\_t~*}, \Type{void~*});\\ +\Type{void}~\Func{\_UPT\_put\_unwind\_info}(\Type{unw\_addr\_space\_t}, \Type{unw\_proc\_info\_t~*}, \Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_get\_dyn\_info\_list\_addr}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_get\_dyn\_info\_list\_addr}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_access\_mem}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_access\_mem}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_access\_reg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_access\_reg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_word\_t~*}, \Type{int}, \Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_access\_fpreg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_fpreg\_t~*}, \Type{int}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_access\_fpreg}(\Type{unw\_addr\_space\_t}, \Type{unw\_regnum\_t}, \Type{unw\_fpreg\_t~*}, \Type{int}, \Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_get\_proc\_name}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{char~*}, \Type{size\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_get\_proc\_name}(\Type{unw\_addr\_space\_t}, \Type{unw\_word\_t}, \Type{char~*}, \Type{size\_t}, \Type{unw\_word\_t~*}, \Type{void~*});\\ \noindent -\Type{int} \Func{\_UPT\_resume}(\Type{unw\_addr\_space\_t}, \Type{unw\_cursor\_t~*}, \Type{void~*});\\ +\Type{int}~\Func{\_UPT\_resume}(\Type{unw\_addr\_space\_t}, \Type{unw\_cursor\_t~*}, \Type{void~*});\\ \section{Description} -The \Func{ptrace}(2) system-call makes it possible for a process to -gain access to the machine-state and virtual memory of \emph{another} -process. With the right set of call-back routines, it is therefore +The \Func{ptrace}(2) system call makes it possible for a process to +gain access to the machine state and virtual memory of \emph{another} +process. With the right set of callback routines, it is therefore possible to hook up \Prog{libunwind} to another process via \Func{ptrace}(2). While it's not very difficult to do so directly, \Prog{libunwind} further facilitates this task by providing ready-to-use callbacks for this purpose. The routines and variables -implementing this facility use a name-prefix of \Func{\_UPT}, which is -stands for ``unwind-via-ptrace''. - -An application that wants to use the \Func{\_UPT}-facility first needs -to create a new \Prog{libunwind} address-space that represents the -target process. This is done by calling -\Func{unw\_create\_addr\_space}(). In many cases, the application -will simply want to pass the address of \Var{\_UPT\_accessors} as the -first argument to this routine. Doing so will ensure that -\Prog{libunwind} will be able to properly unwind the target process. -However, in special circumstances, an application may prefer to use -only portions of the \Prog{\_UPT}-facility. For this reason, the -individual callback routines (\Func{\_UPT\_find\_proc\_info}(), -\Func{\_UPT\_put\_unwind\_info}(), etc.) are also available for direct -use. Of course, the addresses of these routines could also be picked -up from \Var{\_UPT\_accessors}, but doing so would prevent static -initialization. Also, when using \Var{\_UPT\_accessors}, \emph{all} -the callback routines will be linked into the application, even if -they are never actually called. - -Next, the application can turn on ptrace-mode on the target process, -either by forking a new process, invoking \Const{PTRACE\_TRACEME}, and -then starting the target program (via \Func{execve}(2)), or by -directly attaching to an already running process (via -\Const{PTRACE\_ATTACH}). Either way, once the process-ID (pid) of the -target process is known, a \Prog{\_UPT}-info-structure can be created -by calling \Func{\_UPT\_create}(), passing the pid of the target process -as the only argument. The returned void-pointer then needs to be -passed as the ``argument'' pointer (third argument) to -\Func{unw\_init\_remote}(). - -The \Func{\_UPT\_resume}() routine can be used to resume execution of -the target process. It simply invokes \Func{ptrace}(2) with a command -value of \Const{PTRACE\_CONT}. - -When the application is done using \Prog{libunwind} on the target -process, \Func{\_UPT\_destroy}() needs to be called, passing it the -void-pointer that was returned by the corresponding call to -\Func{\_UPT\_create}(). This ensures that all memory and other -resources are freed up. +implementing this facility use a name prefix of \Func{\_UPT}, which is +stands for ``unwind via ptrace''. + +An application that wants to use the \Prog{libunwind} ptrace remote needs to +take the folowing steps. +\begin{enumerate} + + \item Create a new \Prog{libunwind} address space that represents the target + process. This is done by calling \Func{unw\_create\_addr\_space}(). In + many cases, the application will simply want to pass the address of + \Var{\_UPT\_accessors} as the first argument to this routine. Doing so + will ensure that \Prog{libunwind} will be able to properly unwind the + target process. + + \item Turn on ptrace mode on the target process, either by forking a new + process, invoking \Const{PTRACE\_TRACEME}, and then starting the target + program (via \Func{execve}(2)), or by directly attaching to an already + running process (via \Const{PTRACE\_ATTACH}). + + \item Once the process-ID (pid) of the target process is known, a + UPT info structure can be created by calling + \Func{\_UPT\_create}(), passing the pid of the target process as the + only argument. + + \item The opaque pointer returned then needs to be passed as the + ``argument'' pointer (third argument) to \Func{unw\_init\_remote}(). + +\end{enumerate} + +In special circumstances, an application may prefer to use only +portions of the \Prog{libunwind} ptrace remote. For this reason, the individual +callback routines (\Func{\_UPT\_find\_proc\_info}(), +\Func{\_UPT\_put\_unwind\_info}(), etc.) are also available for direct use. Of +course, the addresses of these routines could also be picked up from +\Var{\_UPT\_accessors}, but doing so would prevent static initialization. Also, +when using \Var{\_UPT\_accessors}, \emph{all} the callback routines will be +linked into the application, even if they are never actually called. + +The \Func{\_UPT\_resume}() routine can be used to resume execution of the target +process. It simply invokes \Func{ptrace}(2) with a command value of +\Const{PTRACE\_CONT}. + +When the application is done using \Prog{libunwind} on the target process, +\Func{\_UPT\_destroy}() needs to be called, passing it the opaque pointer that +was returned by the call to \Func{\_UPT\_create}(). This ensures that all +memory and other resources are freed up. \section{Availability} -Since \Func{ptrace}(2) works within a single machine only, the -\Prog{\_UPT}-facility by definition is not available in -\Prog{libunwind}-versions configured for cross-unwinding. +Since \Func{ptrace}(2) works within a single machine only, the libunwind ptrace +remote by definition is not available in \Prog{libunwind} versions configured +for cross-unwinding. \section{Thread Safety} -The \Prog{\_UPT}-facility assumes that a single \Prog{\_UPT}-info -structure is never shared between threads. Because of this, no -explicit locking is used. As long as only one thread uses -a \Prog{\_UPT}-info structure at any given time, this facility -is thread-safe. +The \Prog{libunwind} ptrace remote assumes that a single UPT info structure is +never shared between threads. Because of this, no explicit locking is used. As +long as only one thread uses a UPT info structure at any given time, this +facility is thread-safe. \section{Return Value} -\Func{\_UPT\_create}() may return a \Const{NULL} pointer if it fails -to create the \Prog{\_UPT}-info-structure for any reason. For the -current implementation, the only reason this call may fail is when the -system is out of memory. +\Func{\_UPT\_create}() may return a \Const{NULL} pointer if it fails to create +the UPT info structure for any reason. For the current implementation, the only +reason this call may fail is when the system is out of memory. \section{Files} \begin{Description} -\item[\File{libunwind-ptrace.h}] Headerfile to include when using the - interface defined by this library. +\item[\File{libunwind-ptrace.h}] Header file to include when using the + interface defined by this library. \item[\Opt{-l}\File{unwind-ptrace} \Opt{-l}\File{unwind-generic}] - Linker-switches to add when building a program that uses the + Linker switches to add when building a program that uses the functions defined by this library. \end{Description} +\section{Example} +\begin{verbatim} + #include + #include + #include + + int + main (int argc, char **argv) + { + if (argc != 2) { + fprintf (stderr, "usage: %s PID\n", argv[0]); + exit (EXIT_FAILURE); + } + + char *endptr; + pid_t target_pid = strtoul (argv[1], &endptr, 10); + if (target_pid == 0 && argv[1] == endptr) { + fprintf (stderr, "usage: %s PID\n", argv[0]); + exit (EXIT_FAILURE); + } + + unw_addr_space_t as = unw_create_addr_space (&_UPT_accessors, 0); + if (!as) { + fprintf (stderr, "unw_create_addr_space() failed"); + exit (EXIT_FAILURE); + } + + void *ui = _UPT_create (target_pid); + if (!ui) { + fprintf (stderr, "_UPT_create() failed"); + exit (EXIT_FAILURE); + } + + unw_cursor_t cursor; + int ret = unw_init_remote (&cursor, as, ui); + if (ret < 0) { + fprintf (stderr, "unw_init_remote() failed: ret=%d\n", ret); + exit (EXIT_FAILURE); + } + + do { + unw_proc_info_t pi; + ret = unw_get_proc_info (&cursor, &pi); + if (ret == -UNW_ENOINFO) { + fprintf (stdout, "no info\n"); + } else if (ret >= 0) { + printf ("\tproc=%#016lx-%#016lx\thandler=%#016lx lsda=%#016lx", + (long) pi.start_ip, (long) pi.end_ip, + (long) pi.handler, (long) pi.lsda); + } + ret = unw_step (&cursor); + } while (ret > 0); + if (ret < 0) { + fprintf (stderr, "unwind failed with ret=%d\n", ret); + exit (EXIT_FAILURE); + } + + _UPT_destroy (ui); + unw_destroy_addr_space (as); + exit (EXIT_SUCCESS); + } +\end{verbatim} + \section{See Also} -execve(2), -\SeeAlso{libunwind(3)}, -ptrace(2) +\SeeAlso{execve}(2), +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{ptrace}(2) \section{Author} diff --git a/src/native/external/libunwind/doc/libunwind-setjmp.man b/src/native/external/libunwind/doc/libunwind-setjmp.man index 1faa38e475ddba..2fd4480867f5a0 100644 --- a/src/native/external/libunwind/doc/libunwind-setjmp.man +++ b/src/native/external/libunwind/doc/libunwind-setjmp.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:44 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 10:53:41 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "LIBUNWIND\-SETJMP" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "LIBUNWIND\-SETJMP" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME libunwind\-setjmp \-\- libunwind\-based non\-local gotos @@ -115,11 +117,13 @@ goto routines. .PP .SH SEE ALSO -.PP -libunwind(3), -setjmp(3), longjmp(3), -_setjmp(3), _longjmp(3), -sigsetjmp(3), siglongjmp(3) +libunwind(3libunwind), +setjmp(3), +longjmp(3), +_setjmp(3), +_longjmp(3), +sigsetjmp(3), +siglongjmp(3) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/libunwind-setjmp.tex b/src/native/external/libunwind/doc/libunwind-setjmp.tex index 17ce073186a6c3..2dc9575f80378a 100644 --- a/src/native/external/libunwind/doc/libunwind-setjmp.tex +++ b/src/native/external/libunwind/doc/libunwind-setjmp.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{libunwind-setjmp}{David Mosberger-Tang}{Programming Library}{libunwind-based non-local gotos}libunwind-setjmp -- libunwind-based non-local gotos +\begin{Name}{3libunwind}{libunwind-setjmp}{David Mosberger-Tang}{Programming Library}{libunwind-based non-local gotos}libunwind-setjmp -- libunwind-based non-local gotos \end{Name} \section{Synopsis} @@ -70,11 +70,10 @@ \section{Files} \section{See Also} - -\SeeAlso{libunwind(3)}, -setjmp(3), longjmp(3), -\_setjmp(3), \_longjmp(3), -sigsetjmp(3), siglongjmp(3) +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{setjmp}(3), \SeeAlso{longjmp}(3), +\SeeAlso{\_setjmp}(3), \SeeAlso{\_longjmp}(3), +\SeeAlso{sigsetjmp}(3), \SeeAlso{siglongjmp}(3) \section{Author} diff --git a/src/native/external/libunwind/doc/libunwind.man b/src/native/external/libunwind/doc/libunwind.man index 837e6e31e9a2d3..96efb04fb85c14 100644 --- a/src/native/external/libunwind/doc/libunwind.man +++ b/src/native/external/libunwind/doc/libunwind.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 11:06:25 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "LIBUNWIND" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "LIBUNWIND" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME libunwind \-\- a (mostly) platform\-independent unwind API @@ -129,7 +131,7 @@ unwinding. Say you want to unwind the stack while executing in some function F(). In this function, you would call unw_getcontext() -to get a snapshot of the CPU registers (machine\-state). Then you +to get a snapshot of the CPU registers (machine state). Then you initialize an \fIunwind cursor\fP based on this snapshot. This is done with a call to unw_init_local(). @@ -140,7 +142,7 @@ The unwind cursor can then be moved ``up\&'' (towards earlier stack frames) by calling unw_step(). By repeatedly calling this routine, you can -uncover the entire call\-chain that led to the activation of function +uncover the entire call chain that led to the activation of function F(). A positive return value from unw_step() indicates @@ -166,7 +168,7 @@ back prev to curr whenever that is necessary. In the most extreme case, a program could maintain a separate cursor for each call -frame and that way it could move up and down the callframe\-chain at +frame and that way it could move up and down the callframe chain at will. .PP Given an unwind cursor, it is possible to read and write the CPU @@ -226,14 +228,14 @@ before including the headerfile It is perfectly OK for a single program to employ both local\-only and generic unwinding. That is, whether or not UNW_LOCAL_ONLY -is defined is a choice that each source\-file -(compilation\-unit) can make on its own. Independent of the setting(s) +is defined is a choice that each source file +(compilation unit) can make on its own. Independent of the setting(s) of UNW_LOCAL_ONLY, you\&'ll always link the same library into the program (normally \fB\-l\fPunwind). Furthermore, the portion of libunwind -that manages unwind\-info for dynamically +that manages unwind info for dynamically generated code is not affected by the setting of UNW_LOCAL_ONLY\&. .PP @@ -272,12 +274,12 @@ Remote unwinding is typically used by debuggers and instruction\-set simulators, for example. .PP Before you can unwind a remote process, you need to create a new -address\-space object for that process. This is achieved with the +address space object for that process. This is achieved with the unw_create_addr_space() routine. The routine takes two arguments: a pointer to a set of \fIaccessor\fP routines and an -integer that specifies the byte\-order of the target process. The +integer that specifies the byte order of the target process. The accessor routines provide libunwind with the means to communicate with the remote process. In particular, there are @@ -288,7 +290,7 @@ With the address space created, unwinding can be initiated by a call to unw_init_remote(). This routine is very similar to unw_init_local(), -except that it takes an address\-space +except that it takes an address space object and an opaque pointer as arguments. The routine uses these arguments to fetch the initial machine state. Libunwind never @@ -299,7 +301,7 @@ select, e.g., the thread within a process that is to be unwound. Once a cursor has been initialized with unw_init_remote(), unwinding works exactly like in the local case. That is, you can use unw_step() -to move ``up\&'' in the call\-chain, read and write +to move ``up\&'' in the call chain, read and write registers, or resume execution at a particular stack frame by calling unw_resume\&. .PP @@ -362,7 +364,7 @@ simultaneously. However, any given cursor may be accessed by only one thread at any given time. .PP -To ensure thread\-safety, some libunwind +To ensure thread safety, some libunwind routines may have to use locking. Such routines \fImust not\fP be called from signal @@ -374,7 +376,7 @@ any routine that may be needed for \fIlocal\fP unwinding is signal\-safe (e.g., unw_step() for local unwinding is -signal\-safe). For remote\-unwinding, \fInone\fP +signal\-safe). For remote unwinding, \fInone\fP of the libunwind routines are guaranteed to be signal\-safe. @@ -396,15 +398,15 @@ high\-level language exception handling may not work as expected. .PP The interface for registering and canceling dynamic unwind info has been designed for maximum efficiency, so as to minimize the -performance impact on JIT\-compilers. In particular, both routines are +performance impact on JIT compilers. In particular, both routines are guaranteed to execute in ``constant time\&'' (O(1)) and the -data\-structure encapsulating the dynamic unwind info has been designed +data structure encapsulating the dynamic unwind info has been designed to facilitate sharing, such that similar procedures can share much of the underlying information. .PP For more information on the libunwind support for dynamically -generated code, see libunwind\-dynamic(3)\&. +generated code, see libunwind\-dynamic(3libunwind). .PP .SH CACHING OF UNWIND INFO @@ -417,7 +419,7 @@ using stale data. For example, this would happen if libunwind were to cache information about a shared library which later on gets -unloaded (e.g., via \fIdlclose\fP(3)). +unloaded (e.g., via \fIdlclose\fP(3libunwind)). .PP To prevent the risk of using stale data, libunwind provides two @@ -428,7 +430,7 @@ unw_flush_cache(). The second facility is provided by unw_set_caching_policy(), which lets a program -select the exact caching policy in use for a given address\-space +select the exact caching policy in use for a given address space object. In particular, by selecting the policy UNW_CACHE_NONE, it is possible to turn off caching @@ -455,11 +457,11 @@ should be included. .TP \fB\-l\fPunwind - Linker\-switch to add when building a + Linker switch to add when building a program that does native (same platform) unwinding. .TP \fB\-l\fPunwind\-PLAT - Linker\-switch to add when + Linker switch to add when building a program that unwinds a program on platform PLAT\&. For example, to (cross\-)unwind an IA\-64 program, the linker switch \-lunwind\-ia64 @@ -470,33 +472,33 @@ multiple platforms. .SH SEE ALSO .PP -libunwind\-dynamic(3), -libunwind\-ia64(3), -libunwind\-ptrace(3), -libunwind\-setjmp(3), -unw_create_addr_space(3), -unw_destroy_addr_space(3), -unw_flush_cache(3), -unw_get_accessors(3), -unw_get_fpreg(3), -unw_get_proc_info(3), -unw_get_proc_name(3), -unw_get_reg(3), -unw_getcontext(3), -unw_init_local(3), -unw_init_remote(3), -unw_is_fpreg(3), -unw_is_signal_frame(3), -unw_regname(3), -unw_resume(3), -unw_set_caching_policy(3), -unw_set_cache_size(3), -unw_set_fpreg(3), -unw_set_reg(3), -unw_step(3), -unw_strerror(3), -_U_dyn_register(3), -_U_dyn_cancel(3) +libunwind\-dynamic(3libunwind), +libunwind\-ia64(3libunwind), +libunwind\-ptrace(3libunwind), +libunwind\-setjmp(3libunwind), +unw_create_addr_space(3libunwind), +unw_destroy_addr_space(3libunwind), +unw_flush_cache(3libunwind), +unw_get_accessors(3libunwind), +unw_get_fpreg(3libunwind), +unw_get_proc_info(3libunwind), +unw_get_proc_name(3libunwind), +unw_get_reg(3libunwind), +unw_getcontext(3libunwind), +unw_init_local(3libunwind), +unw_init_remote(3libunwind), +unw_is_fpreg(3libunwind), +unw_is_signal_frame(3libunwind), +unw_regname(3libunwind), +unw_resume(3libunwind), +unw_set_caching_policy(3libunwind), +unw_set_cache_size(3libunwind), +unw_set_fpreg(3libunwind), +unw_set_reg(3libunwind), +unw_step(3libunwind), +unw_strerror(3libunwind), +_U_dyn_register(3libunwind), +_U_dyn_cancel(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/libunwind.tex b/src/native/external/libunwind/doc/libunwind.tex index 8809ba5a36f83c..95d43a5317393e 100644 --- a/src/native/external/libunwind/doc/libunwind.tex +++ b/src/native/external/libunwind/doc/libunwind.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{libunwind}{David Mosberger-Tang}{Programming Library}{Introduction to libunwind}libunwind -- a (mostly) platform-independent unwind API +\begin{Name}{3libunwind}{libunwind}{David Mosberger-Tang}{Programming Library}{Introduction to libunwind}libunwind -- a (mostly) platform-independent unwind API \end{Name} \section{Synopsis} @@ -69,14 +69,14 @@ \section{Local Unwinding} within a running program. This is called \emph{local} unwinding. Say you want to unwind the stack while executing in some function \Func{F}(). In this function, you would call \Func{unw\_getcontext}() -to get a snapshot of the CPU registers (machine-state). Then you +to get a snapshot of the CPU registers (machine state). Then you initialize an \emph{unwind~cursor} based on this snapshot. This is done with a call to \Func{unw\_init\_local}(). The cursor now points to the current frame, that is, the stack frame that corresponds to the current activation of function \Func{F}(). The unwind cursor can then be moved ``up'' (towards earlier stack frames) by calling \Func{unw\_step}(). By repeatedly calling this routine, you can -uncover the entire call-chain that led to the activation of function +uncover the entire call chain that led to the activation of function \Func{F}(). A positive return value from \Func{unw\_step}() indicates that there are more frames in the chain, zero indicates that the end of the chain has been reached, and any negative value indicates that @@ -93,7 +93,7 @@ \section{Local Unwinding} approach, the program could move one step ``down'' simply by copying back \Var{prev} to \Var{curr} whenever that is necessary. In the most extreme case, a program could maintain a separate cursor for each call -frame and that way it could move up and down the callframe-chain at +frame and that way it could move up and down the callframe chain at will. Given an unwind cursor, it is possible to read and write the CPU @@ -135,11 +135,11 @@ \section{Local Unwinding} \Const{UNW\_LOCAL\_ONLY} before including the headerfile \File{$<$libunwind.h$>$}. It is perfectly OK for a single program to employ both local-only and generic unwinding. That is, whether or not -\Const{UNW\_LOCAL\_ONLY} is defined is a choice that each source-file -(compilation-unit) can make on its own. Independent of the setting(s) +\Const{UNW\_LOCAL\_ONLY} is defined is a choice that each source file +(compilation unit) can make on its own. Independent of the setting(s) of \Const{UNW\_LOCAL\_ONLY}, you'll always link the same library into the program (normally \Opt{-l}\File{unwind}). Furthermore, the -portion of \Prog{libunwind} that manages unwind-info for dynamically +portion of \Prog{libunwind} that manages unwind info for dynamically generated code is not affected by the setting of \Const{UNW\_LOCAL\_ONLY}. @@ -175,10 +175,10 @@ \section{Remote Unwinding} used by debuggers and instruction-set simulators, for example. Before you can unwind a remote process, you need to create a new -address-space object for that process. This is achieved with the +address space object for that process. This is achieved with the \Func{unw\_create\_addr\_space}() routine. The routine takes two arguments: a pointer to a set of \emph{accessor} routines and an -integer that specifies the byte-order of the target process. The +integer that specifies the byte order of the target process. The accessor routines provide \Func{libunwind} with the means to communicate with the remote process. In particular, there are callbacks to read and write the process's memory, its registers, and @@ -186,7 +186,7 @@ \section{Remote Unwinding} With the address space created, unwinding can be initiated by a call to \Func{unw\_init\_remote}(). This routine is very similar to -\Func{unw\_init\_local}(), except that it takes an address-space +\Func{unw\_init\_local}(), except that it takes an address space object and an opaque pointer as arguments. The routine uses these arguments to fetch the initial machine state. \Prog{Libunwind} never uses the opaque pointer on its own, but instead just passes it on to @@ -195,7 +195,7 @@ \section{Remote Unwinding} Once a cursor has been initialized with \Func{unw\_init\_remote}(), unwinding works exactly like in the local case. That is, you can use -\Func{unw\_step}() to move ``up'' in the call-chain, read and write +\Func{unw\_step}() to move ``up'' in the call chain, read and write registers, or resume execution at a particular stack frame by calling \Func{unw\_resume}. @@ -242,14 +242,14 @@ \section{Thread- and Signal-Safety} However, any given cursor may be accessed by only one thread at any given time. -To ensure thread-safety, some \Prog{libunwind} routines may have to +To ensure thread safety, some \Prog{libunwind} routines may have to use locking. Such routines \emph{must~not} be called from signal handlers (directly or indirectly) and are therefore \emph{not} signal-safe. The manual page for each \Prog{libunwind} routine identifies whether or not it is signal-safe, but as a general rule, any routine that may be needed for \emph{local} unwinding is signal-safe (e.g., \Func{unw\_step}() for local unwinding is -signal-safe). For remote-unwinding, \emph{none} of the +signal-safe). For remote unwinding, \emph{none} of the \Prog{libunwind} routines are guaranteed to be signal-safe. @@ -265,14 +265,14 @@ \section{Unwinding Through Dynamically Generated Code} The interface for registering and canceling dynamic unwind info has been designed for maximum efficiency, so as to minimize the -performance impact on JIT-compilers. In particular, both routines are +performance impact on JIT compilers. In particular, both routines are guaranteed to execute in ``constant time'' (O(1)) and the -data-structure encapsulating the dynamic unwind info has been designed +data structure encapsulating the dynamic unwind info has been designed to facilitate sharing, such that similar procedures can share much of the underlying information. For more information on the \Prog{libunwind} support for dynamically -generated code, see \SeeAlso{libunwind-dynamic(3)}. +generated code, see \SeeAlso{libunwind-dynamic}(3libunwind). \section{Caching of Unwind Info} @@ -282,7 +282,7 @@ \section{Caching of Unwind Info} during its lifetime, this creates a risk of \Prog{libunwind} using stale data. For example, this would happen if \Prog{libunwind} were to cache information about a shared library which later on gets -unloaded (e.g., via \Cmd{dlclose}{3}). +unloaded (e.g., via \Cmd{dlclose}{3libunwind}). To prevent the risk of using stale data, \Prog{libunwind} provides two facilities: first, it is possible to flush the cached information @@ -290,7 +290,7 @@ \section{Caching of Unwind Info} entire address space, if desired). This functionality is provided by \Func{unw\_flush\_cache}(). The second facility is provided by \Func{unw\_set\_caching\_policy}(), which lets a program -select the exact caching policy in use for a given address-space +select the exact caching policy in use for a given address space object. In particular, by selecting the policy \Const{UNW\_CACHE\_NONE}, it is possible to turn off caching completely, therefore eliminating the risk of stale data altogether @@ -308,9 +308,9 @@ \section{Files} the unwind target runs on platform \Var{PLAT}. For example, to unwind an IA-64 program, the header file \File{libunwind-ia64.h} should be included. -\item[\Opt{-l}\File{unwind}] Linker-switch to add when building a +\item[\Opt{-l}\File{unwind}] Linker switch to add when building a program that does native (same platform) unwinding. -\item[\Opt{-l}\File{unwind-}\Var{PLAT}] Linker-switch to add when +\item[\Opt{-l}\File{unwind-}\Var{PLAT}] Linker switch to add when building a program that unwinds a program on platform \Var{PLAT}. For example, to (cross-)unwind an IA-64 program, the linker switch \File{-lunwind-ia64} should be added. Note: multiple such switches @@ -320,33 +320,33 @@ \section{Files} \section{See Also} -\SeeAlso{libunwind-dynamic(3)}, -\SeeAlso{libunwind-ia64(3)}, -\SeeAlso{libunwind-ptrace(3)}, -\SeeAlso{libunwind-setjmp(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_destroy\_addr\_space(3)}, -\SeeAlso{unw\_flush\_cache(3)}, -\SeeAlso{unw\_get\_accessors(3)}, -\SeeAlso{unw\_get\_fpreg(3)}, -\SeeAlso{unw\_get\_proc\_info(3)}, -\SeeAlso{unw\_get\_proc\_name(3)}, -\SeeAlso{unw\_get\_reg(3)}, -\SeeAlso{unw\_getcontext(3)}, -\SeeAlso{unw\_init\_local(3)}, -\SeeAlso{unw\_init\_remote(3)}, -\SeeAlso{unw\_is\_fpreg(3)}, -\SeeAlso{unw\_is\_signal\_frame(3)}, -\SeeAlso{unw\_regname(3)}, -\SeeAlso{unw\_resume(3)}, -\SeeAlso{unw\_set\_caching\_policy(3)}, -\SeeAlso{unw\_set\_cache\_size(3)}, -\SeeAlso{unw\_set\_fpreg(3)}, -\SeeAlso{unw\_set\_reg(3)}, -\SeeAlso{unw\_step(3)}, -\SeeAlso{unw\_strerror(3)}, -\SeeAlso{\_U\_dyn\_register(3)}, -\SeeAlso{\_U\_dyn\_cancel(3)} +\SeeAlso{libunwind-dynamic}(3libunwind), +\SeeAlso{libunwind-ia64}(3libunwind), +\SeeAlso{libunwind-ptrace}(3libunwind), +\SeeAlso{libunwind-setjmp}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_destroy\_addr\_space}(3libunwind), +\SeeAlso{unw\_flush\_cache}(3libunwind), +\SeeAlso{unw\_get\_accessors}(3libunwind), +\SeeAlso{unw\_get\_fpreg}(3libunwind), +\SeeAlso{unw\_get\_proc\_info}(3libunwind), +\SeeAlso{unw\_get\_proc\_name}(3libunwind), +\SeeAlso{unw\_get\_reg}(3libunwind), +\SeeAlso{unw\_getcontext}(3libunwind), +\SeeAlso{unw\_init\_local}(3libunwind), +\SeeAlso{unw\_init\_remote}(3libunwind), +\SeeAlso{unw\_is\_fpreg}(3libunwind), +\SeeAlso{unw\_is\_signal\_frame}(3libunwind), +\SeeAlso{unw\_regname}(3libunwind), +\SeeAlso{unw\_resume}(3libunwind), +\SeeAlso{unw\_set\_caching\_policy}(3libunwind), +\SeeAlso{unw\_set\_cache\_size}(3libunwind), +\SeeAlso{unw\_set\_fpreg}(3libunwind), +\SeeAlso{unw\_set\_reg}(3libunwind), +\SeeAlso{unw\_step}(3libunwind), +\SeeAlso{unw\_strerror}(3libunwind), +\SeeAlso{\_U\_dyn\_register}(3libunwind), +\SeeAlso{\_U\_dyn\_cancel}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_apply_reg_state.man b/src/native/external/libunwind/doc/unw_apply_reg_state.man index 457f0c4dd17a47..10e02baa29c021 100644 --- a/src/native/external/libunwind/doc/unw_apply_reg_state.man +++ b/src/native/external/libunwind/doc/unw_apply_reg_state.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Wed Aug 16 11:09:44 PDT 2017 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_APPLY\\_REG\\_STATE" "3" "16 August 2017" "Programming Library " "Programming Library " +.TH "UNW\\_APPLY\\_REG\\_STATE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_apply_reg_state \-\- apply a register state update to a cursor @@ -39,16 +41,16 @@ which have been obtained by calling unw_reg_states_iterate\&. .PP On successful completion, unw_apply_reg_state() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_apply_reg_state() -is thread\-safe. If cursor cp +is thread safe. If cursor cp is -in the local address\-space, this routine is also safe to use from a +in the local address space, this routine is also safe to use from a signal handler. .PP .SH ERRORS @@ -64,20 +66,20 @@ was unable to locate unwind\-info for the procedure. .TP UNW_EBADVERSION - The unwind\-info for the procedure has + The unwind info for the procedure has version or format that is not understood by libunwind\&. .PP In addition, unw_apply_reg_state() may return any error returned by the access_mem() call\-back (see -unw_create_addr_space(3)). +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_reg_states_iterate(3) +libunwind(3libunwind), +unw_reg_states_iterate(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_apply_reg_state.tex b/src/native/external/libunwind/doc/unw_apply_reg_state.tex index c67cc3ebfafdeb..b109afd392359d 100644 --- a/src/native/external/libunwind/doc/unw_apply_reg_state.tex +++ b/src/native/external/libunwind/doc/unw_apply_reg_state.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_apply\_reg\_state}{David Mosberger-Tang}{Programming Library}{unw\_apply\_reg\_state}unw\_apply\_reg\_state -- apply a register state update to a cursor +\begin{Name}{3libunwind}{unw\_apply\_reg\_state}{David Mosberger-Tang}{Programming Library}{unw\_apply\_reg\_state}unw\_apply\_reg\_state -- apply a register state update to a cursor \end{Name} \section{Synopsis} @@ -25,13 +25,13 @@ \section{Description} \section{Return Value} On successful completion, \Func{unw\_apply\_reg\_state}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_apply\_reg\_state}() is thread-safe. If cursor \Var{cp} is -in the local address-space, this routine is also safe to use from a +\Func{unw\_apply\_reg\_state}() is thread safe. If cursor \Var{cp} is +in the local address space, this routine is also safe to use from a signal handler. \section{Errors} @@ -40,17 +40,17 @@ \section{Errors} \item[\Const{UNW\_EUNSPEC}] An unspecified error occurred. \item[\Const{UNW\_ENOINFO}] \Prog{Libunwind} was unable to locate unwind-info for the procedure. -\item[\Const{UNW\_EBADVERSION}] The unwind-info for the procedure has +\item[\Const{UNW\_EBADVERSION}] The unwind info for the procedure has version or format that is not understood by \Prog{libunwind}. \end{Description} In addition, \Func{unw\_apply\_reg\_state}() may return any error returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_reg\_states\_iterate(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_reg\_states\_iterate}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_backtrace.man b/src/native/external/libunwind/doc/unw_backtrace.man index e1a4a9dfcad761..26b643a649c956 100644 --- a/src/native/external/libunwind/doc/unw_backtrace.man +++ b/src/native/external/libunwind/doc/unw_backtrace.man @@ -1,7 +1,7 @@ .\" *********************************** start of \input{common.tex} .\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Jan 5 15:33:00 2023 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -12,7 +12,7 @@ .fi .. -.TH "UNW\\_BACKTRACE" "3" "05 January 2023" "Programming Library " "Programming Library " +.TH "UNW\\_BACKTRACE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_backtrace \-\- return backtrace for the calling program @@ -91,8 +91,8 @@ stored. .SH SEE ALSO .PP -libunwind(3), -unw_step(3) +libunwind(3libunwind), +unw_step(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_backtrace.tex b/src/native/external/libunwind/doc/unw_backtrace.tex index 3e5152b6ef0bb8..4bd396ba38a428 100644 --- a/src/native/external/libunwind/doc/unw_backtrace.tex +++ b/src/native/external/libunwind/doc/unw_backtrace.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_backtrace}{David Mosberger-Tang}{Programming Library}{unw\_backtrace}unw\_backtrace -- return backtrace for the calling program +\begin{Name}{3libunwind}{unw\_backtrace}{David Mosberger-Tang}{Programming Library}{unw\_backtrace}unw\_backtrace -- return backtrace for the calling program \end{Name} \section{Synopsis} @@ -34,8 +34,8 @@ \section{Description} calling \Func{unw\_backtrace}(). In case you want to obtain the backtrace from a specific \Type{unw\_context\_t}, -you can call \Func{unw\_backtrace2} with that context passing \Const{0} for flag. -If the \Type{unw\_context_t} is known to be a signal frame (i.e., from the third argument +you can call \Func{unw\_backtrace2} with that context passing \Const{0} for flag. +If the \Type{unw\_context\_t} is known to be a signal frame (i.e., from the third argument in a sigaction handler on linux), \Func{unw\_backtrace2} can be used to collect only the frames before the signal frame passing the \Const{UNW\_INIT\_SIGNAL\_FRAME} flag. @@ -47,8 +47,8 @@ \section{Return Value} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_step(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_step}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_create_addr_space.man b/src/native/external/libunwind/doc/unw_create_addr_space.man index 4aca13ecd8297e..b04be571da4d9c 100644 --- a/src/native/external/libunwind/doc/unw_create_addr_space.man +++ b/src/native/external/libunwind/doc/unw_create_addr_space.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 10:53:41 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_CREATE\\_ADDR\\_SPACE" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_CREATE\\_ADDR\\_SPACE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_create_addr_space \-\- create address space for remote unwinding @@ -32,54 +34,54 @@ byteorder); .PP The unw_create_addr_space() routine creates a new unwind -address\-space and initializes it based on the call\-back routines +address space and initializes it based on the callback routines passed via the ap pointer and the specified byteorder\&. -The call\-back routines are described in detail below. The +The callback routines are described in detail below. The byteorder -can be set to 0 to request the default byte\-order of -the unwind target. To request a particular byte\-order, +can be set to 0 to request the default byte order of +the unwind target. To request a particular byte order, byteorder can be set to any constant defined by \&. -In particular, __LITTLE_ENDIAN +In particular, UNW_LITTLE_ENDIAN would -request little\-endian byte\-order and __BIG_ENDIAN +request little\-endian byte order and UNW_BIG_ENDIAN would -request big\-endian byte\-order. Whether or not a particular byte\-order +request big\-endian byte order. Whether or not a particular byte order is supported depends on the target platform. .PP -.SH CALL\-BACK ROUTINES +.SH CALLBACK ROUTINES .PP Libunwind -uses a set of call\-back routines to access the -information it needs to unwind a chain of stack\-frames. These +uses a set of callback routines to access the +information it needs to unwind a chain of stack frames. These routines are specified via the ap argument, which points to a variable of type unw_accessors_t\&. The contents of this -variable is copied into the newly\-created address space, so the +variable is copied into the newly created address space, so the variable must remain valid only for the duration of the call to unw_create_addr_space(). .PP -The first argument to every call\-back routine is an address\-space +The first argument to every callback routine is an address space identifier (as) and the last argument is an arbitrary, -application\-specified void\-pointer (arg). +application specified void pointer (arg). When invoking a -call\-back routine, libunwind +callback routine, libunwind sets the as argument to the -address\-space on whose behalf the invocation is made and the arg +address space on whose behalf the invocation is made and the arg argument to the value that was specified when -unw_init_remote(3) +unw_init_remote(3libunwind) was called. .PP -The synopsis and a detailed description of every call\-back routine +The synopsis and a detailed description of every callback routine follows below. .PP -.SS CALL\-BACK ROUTINE SYNOPSIS +.SS CALLBACK ROUTINE SYNOPSIS .PP int find_proc_info(unw_addr_space_t @@ -169,19 +171,19 @@ unw_word_t *offp, .PP Libunwind invokes the find_proc_info() -call\-back to +callback to locate the information need to unwind a particular procedure. The ip -argument is an instruction\-address inside the procedure whose +argument is an instruction address inside the procedure whose information is needed. The pip argument is a pointer to the variable used to return the desired information. The type of this variable is unw_proc_info_t\&. See -unw_get_proc_info(3) +unw_get_proc_info(3libunwind) for details. Argument need_unwind_info -is zero if the call\-back does not need to +is zero if the callback does not need to provide values for the following members in the unw_proc_info_t structure: format, @@ -194,22 +196,22 @@ in these members. Furthermore, the contents of the memory addressed by the unwind_info member must remain valid until the info is released via the put_unwind_info -call\-back (see below). +callback (see below). .PP On successful completion, the find_proc_info() -call\-back must +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. In particular, this -call\-back may return \-UNW_ESTOPUNWIND +error codes may be returned. In particular, this +callback may return \-UNW_ESTOPUNWIND to signal the end of -the frame\-chain. +the frame chain. .PP .SS PUT_UNWIND_INFO .PP Libunwind invokes the put_unwind_info() -call\-back to +callback to release the resources (such as memory) allocated by a previous call to find_proc_info() with the need_unwind_info @@ -229,36 +231,36 @@ argument. .PP Libunwind invokes the get_dyn_info_list_addr() -call\-back to obtain the address of the head of the dynamic unwind\-info +callback to obtain the address of the head of the dynamic unwind info registration list. The variable stored at the returned address must have a type of unw_dyn_info_list_t (see -_U_dyn_register(3)). +_U_dyn_register(3libunwind)). The dliap argument is a pointer to a variable of type unw_word_t which is used to return the -address of the dynamic unwind\-info registration list. If no dynamic -unwind\-info registration list exist, the value pointed to by +address of the dynamic unwind info registration list. If no dynamic +unwind info registration list exist, the value pointed to by dliap must be cleared to zero. Libunwind will cache the value returned by get_dyn_info_list_addr() if caching is -enabled for the given address\-space. The cache can be cleared with a +enabled for the given address space. The cache can be cleared with a call to unw_flush_cache(). .PP On successful completion, the get_dyn_info_list_addr() -call\-back must return zero. Otherwise, the negative value of one of +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. +error codes may be returned. .PP .SS ACCESS_MEM .PP Libunwind invokes the access_mem() -call\-back to read -from or write to a word of memory in the target address\-space. The +callback to read +from or write to a word of memory in the target address space. The address of the word to be accessed is passed in argument addr\&. To read memory, libunwind sets argument write @@ -272,21 +274,21 @@ value and valp to point to the word that contains the value to be written. The word that valp points to is always in the -byte\-order of the host\-platform, regardless of the byte\-order of the -target. In other words, it is the responsibility of the call\-back -routine to convert between the target\&'s and the host\&'s byte\-order, if +byte order of the host platform, regardless of the byte order of the +target. In other words, it is the responsibility of the callback +routine to convert between the target\&'s and the host\&'s byte order, if necessary. .PP On successful completion, the access_mem() -call\-back must return zero. Otherwise, the negative value of one of +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. +error codes may be returned. .PP .SS ACCESS_REG .PP Libunwind invokes the access_reg() -call\-back to read +callback to read from or write to a scalar (non\-floating\-point) CPU register. The index of the register to be accessed is passed in argument regnum\&. @@ -301,22 +303,22 @@ write to a non\-zero value and valp to point to the word that contains the value to be written. The word that valp -points to is always in the byte\-order of the host\-platform, regardless -of the byte\-order of the target. In other words, it is the -responsibility of the call\-back routine to convert between the -target\&'s and the host\&'s byte\-order, if necessary. +points to is always in the byte order of the host platform, regardless +of the byte order of the target. In other words, it is the +responsibility of the callback routine to convert between the +target\&'s and the host\&'s byte order, if necessary. .PP On successful completion, the access_reg() -call\-back must +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. +error codes may be returned. .PP .SS ACCESS_FPREG .PP Libunwind invokes the access_fpreg() -call\-back to read +callback to read from or write to a floating\-point CPU register. The index of the register to be accessed is passed in argument regnum\&. To read a @@ -335,62 +337,62 @@ the variable of type unw_fpreg_t that contains the value to be written. The word that fpvalp points to is always in the -byte\-order of the host\-platform, regardless of the byte\-order of the -target. In other words, it is the responsibility of the call\-back -routine to convert between the target\&'s and the host\&'s byte\-order, if +byte order of the host platform, regardless of the byte order of the +target. In other words, it is the responsibility of the callback +routine to convert between the target\&'s and the host\&'s byte order, if necessary. .PP On successful completion, the access_fpreg() -call\-back must +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. +error codes may be returned. .PP .SS RESUME .PP Libunwind invokes the resume() -call\-back to resume +callback to resume execution in the target address space. Argument cp is the -unwind\-cursor that identifies the stack\-frame in which execution +unwind cursor that identifies the stack frame in which execution should resume. By the time libunwind invokes the resume -call\-back, it has already established the desired machine\- and -memory\-state via calls to the access_reg(), +callback, it has already established the desired machine and +memory state via calls to the access_reg(), access_fpreg, and access_mem() -call\-backs. Thus, all -the call\-back needs to do is perform whatever action is needed to +callbacks. Thus, all +the callback needs to do is perform whatever action is needed to actually resume execution. .PP The resume -call\-back is invoked only in response to a call to -unw_resume(3), +callback is invoked only in response to a call to +unw_resume(3libunwind), so applications which never invoke -unw_resume(3) +unw_resume(3libunwind) need not define the resume callback. .PP On successful completion, the resume() -call\-back must return +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. As a special case, -when resuming execution in the local address space, the call\-back will +error codes may be returned. As a special case, +when resuming execution in the local address space, the callback will not return on success. .PP .SS GET_PROC_NAME .PP Libunwind invokes the get_proc_name() -call\-back to -obtain the procedure\-name of a static (not dynamically generated) +callback to +obtain the procedure name of a static (not dynamically generated) procedure. Argument addr -is an instruction\-address within the +is an instruction address within the procedure whose name is to be obtained. The bufp argument is a -pointer to a character\-buffer used to return the procedure name. The +pointer to a character buffer used to return the procedure name. The size of this buffer is specified in argument buf_len\&. The returned name must be terminated by a NUL character. If the @@ -402,7 +404,7 @@ buffer set to the NUL character and \-UNW_ENOMEM must be returned. Argument offp is a pointer to a word which is used to -return the byte\-offset relative to the start of the procedure whose +return the byte offset relative to the start of the procedure whose name is being returned. For example, if procedure foo() starts at address 0x40003000, then invoking get_proc_name() @@ -414,10 +416,10 @@ pointed to by offp bytes long). .PP On successful completion, the get_proc_name() -call\-back must +callback must return zero. Otherwise, the negative value of one of the unw_error_t -error\-codes may be returned. +error codes may be returned. .PP .SH RETURN VALUE @@ -426,7 +428,7 @@ On successful completion, unw_create_addr_space() returns a non\-NULL value that represents the newly created -address\-space. Otherwise, NULL +address space. Otherwise, NULL is returned. .PP .SH THREAD AND SIGNAL SAFETY @@ -439,12 +441,12 @@ safe to use from a signal handler. .SH SEE ALSO .PP -_U_dyn_register(3), -libunwind(3), -unw_destroy_addr_space(3), -unw_get_proc_info(3), -unw_init_remote(3), -unw_resume(3) +_U_dyn_register(3libunwind), +libunwind(3libunwind), +unw_destroy_addr_space(3libunwind), +unw_get_proc_info(3libunwind), +unw_init_remote(3libunwind), +unw_resume(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_create_addr_space.tex b/src/native/external/libunwind/doc/unw_create_addr_space.tex index 8de0691f3a9206..818781d6b862f6 100644 --- a/src/native/external/libunwind/doc/unw_create_addr_space.tex +++ b/src/native/external/libunwind/doc/unw_create_addr_space.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_create\_addr\_space}{David Mosberger-Tang}{Programming Library}{unw\_create\_addr\_space}unw\_create\_addr\_space -- create address space for remote unwinding +\begin{Name}{3libunwind}{unw\_create\_addr\_space}{David Mosberger-Tang}{Programming Library}{unw\_create\_addr\_space}unw\_create\_addr\_space -- create address space for remote unwinding \end{Name} \section{Synopsis} @@ -17,39 +17,39 @@ \section{Synopsis} \section{Description} The \Func{unw\_create\_addr\_space}() routine creates a new unwind -address-space and initializes it based on the call-back routines +address space and initializes it based on the callback routines passed via the \Var{ap} pointer and the specified \Var{byteorder}. -The call-back routines are described in detail below. The -\Var{byteorder} can be set to 0 to request the default byte-order of -the unwind target. To request a particular byte-order, +The callback routines are described in detail below. The +\Var{byteorder} can be set to 0 to request the default byte order of +the unwind target. To request a particular byte order, \Var{byteorder} can be set to any constant defined by -\File{$<$endian.h$>$}. In particular, \Const{\_\_LITTLE\_ENDIAN} would -request little-endian byte-order and \Const{\_\_BIG\_ENDIAN} would -request big-endian byte-order. Whether or not a particular byte-order +\File{$<$endian.h$>$}. In particular, \Const{UNW\_LITTLE\_ENDIAN} would +request little-endian byte order and \Const{UNW\_BIG\_ENDIAN} would +request big-endian byte order. Whether or not a particular byte order is supported depends on the target platform. -\section{Call-back Routines} +\section{Callback Routines} -\Prog{Libunwind} uses a set of call-back routines to access the -information it needs to unwind a chain of stack-frames. These +\Prog{Libunwind} uses a set of callback routines to access the +information it needs to unwind a chain of stack frames. These routines are specified via the \Var{ap} argument, which points to a variable of type \Type{unw\_accessors\_t}. The contents of this -variable is copied into the newly-created address space, so the +variable is copied into the newly created address space, so the variable must remain valid only for the duration of the call to \Func{unw\_create\_addr\_space}(). -The first argument to every call-back routine is an address-space +The first argument to every callback routine is an address space identifier (\Var{as}) and the last argument is an arbitrary, -application-specified void-pointer (\Var{arg}). When invoking a -call-back routine, \Prog{libunwind} sets the \Var{as} argument to the -address-space on whose behalf the invocation is made and the \Var{arg} +application specified void pointer (\Var{arg}). When invoking a +callback routine, \Prog{libunwind} sets the \Var{as} argument to the +address space on whose behalf the invocation is made and the \Var{arg} argument to the value that was specified when -\Func{unw\_init\_remote}(3) was called. +\Func{unw\_init\_remote}(3libunwind) was called. -The synopsis and a detailed description of every call-back routine +The synopsis and a detailed description of every callback routine follows below. -\subsection{Call-back Routine Synopsis} +\subsection{Callback Routine Synopsis} \Type{int} \Func{find\_proc\_info}(\Type{unw\_addr\_space\_t} \Var{as},\\ \SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\SP\Type{unw\_word\_t} \Var{ip}, \Type{unw\_proc\_info\_t~*}\Var{pip},\\ @@ -76,31 +76,31 @@ \subsection{Call-back Routine Synopsis} \subsection{find\_proc\_info} -\Prog{Libunwind} invokes the \Func{find\_proc\_info}() call-back to +\Prog{Libunwind} invokes the \Func{find\_proc\_info}() callback to locate the information need to unwind a particular procedure. The -\Var{ip} argument is an instruction-address inside the procedure whose +\Var{ip} argument is an instruction address inside the procedure whose information is needed. The \Var{pip} argument is a pointer to the variable used to return the desired information. The type of this variable is \Type{unw\_proc\_info\_t}. See -\Func{unw\_get\_proc\_info(3)} for details. Argument -\Var{need\_unwind\_info} is zero if the call-back does not need to +\Func{unw\_get\_proc\_info}(3libunwind) for details. Argument +\Var{need\_unwind\_info} is zero if the callback does not need to provide values for the following members in the \Type{unw\_proc\_info\_t} structure: \Var{format}, \Var{unwind\_info\_size}, and \Var{unwind\_info}. If \Var{need\_unwind\_info} is non-zero, valid values need to be returned in these members. Furthermore, the contents of the memory addressed by the \Var{unwind\_info} member must remain valid until the info is -released via the \Func{put\_unwind\_info} call-back (see below). +released via the \Func{put\_unwind\_info} callback (see below). -On successful completion, the \Func{find\_proc\_info}() call-back must +On successful completion, the \Func{find\_proc\_info}() callback must return zero. Otherwise, the negative value of one of the -\Type{unw\_error\_t} error-codes may be returned. In particular, this -call-back may return -\Const{UNW\_ESTOPUNWIND} to signal the end of -the frame-chain. +\Type{unw\_error\_t} error codes may be returned. In particular, this +callback may return -\Const{UNW\_ESTOPUNWIND} to signal the end of +the frame chain. \subsection{put\_unwind\_info} -\Prog{Libunwind} invokes the \Func{put\_unwind\_info}() call-back to +\Prog{Libunwind} invokes the \Func{put\_unwind\_info}() callback to release the resources (such as memory) allocated by a previous call to \Func{find\_proc\_info}() with the \Var{need\_unwind\_info} argument set to a non-zero value. The \Var{pip} argument has the same value as @@ -113,44 +113,44 @@ \subsection{put\_unwind\_info} \subsection{get\_dyn\_info\_list\_addr} \Prog{Libunwind} invokes the \Func{get\_dyn\_info\_list\_addr}() -call-back to obtain the address of the head of the dynamic unwind-info +callback to obtain the address of the head of the dynamic unwind info registration list. The variable stored at the returned address must have a type of \Type{unw\_dyn\_info\_list\_t} (see -\Func{\_U\_dyn\_register}(3)). The \Var{dliap} argument is a pointer +\Func{\_U\_dyn\_register}(3libunwind)). The \Var{dliap} argument is a pointer to a variable of type \Type{unw\_word\_t} which is used to return the -address of the dynamic unwind-info registration list. If no dynamic -unwind-info registration list exist, the value pointed to by +address of the dynamic unwind info registration list. If no dynamic +unwind info registration list exist, the value pointed to by \Var{dliap} must be cleared to zero. \Prog{Libunwind} will cache the value returned by \Func{get\_dyn\_info\_list\_addr}() if caching is -enabled for the given address-space. The cache can be cleared with a +enabled for the given address space. The cache can be cleared with a call to \Func{unw\_flush\_cache}(). On successful completion, the \Func{get\_dyn\_info\_list\_addr}() -call-back must return zero. Otherwise, the negative value of one of -the \Type{unw\_error\_t} error-codes may be returned. +callback must return zero. Otherwise, the negative value of one of +the \Type{unw\_error\_t} error codes may be returned. \subsection{access\_mem} -\Prog{Libunwind} invokes the \Func{access\_mem}() call-back to read -from or write to a word of memory in the target address-space. The +\Prog{Libunwind} invokes the \Func{access\_mem}() callback to read +from or write to a word of memory in the target address space. The address of the word to be accessed is passed in argument \Var{addr}. To read memory, \Prog{libunwind} sets argument \Var{write} to zero and \Var{valp} to point to the word that receives the read value. To write memory, \Prog{libunwind} sets argument \Var{write} to a non-zero value and \Var{valp} to point to the word that contains the value to be written. The word that \Var{valp} points to is always in the -byte-order of the host-platform, regardless of the byte-order of the -target. In other words, it is the responsibility of the call-back -routine to convert between the target's and the host's byte-order, if +byte order of the host platform, regardless of the byte order of the +target. In other words, it is the responsibility of the callback +routine to convert between the target's and the host's byte order, if necessary. On successful completion, the \Func{access\_mem}() -call-back must return zero. Otherwise, the negative value of one of -the \Type{unw\_error\_t} error-codes may be returned. +callback must return zero. Otherwise, the negative value of one of +the \Type{unw\_error\_t} error codes may be returned. \subsection{access\_reg} -\Prog{Libunwind} invokes the \Func{access\_reg}() call-back to read +\Prog{Libunwind} invokes the \Func{access\_reg}() callback to read from or write to a scalar (non-floating-point) CPU register. The index of the register to be accessed is passed in argument \Var{regnum}. To read a register, \Prog{libunwind} sets argument @@ -158,18 +158,18 @@ \subsection{access\_reg} the read value. To write a register, \Prog{libunwind} sets argument \Var{write} to a non-zero value and \Var{valp} to point to the word that contains the value to be written. The word that \Var{valp} -points to is always in the byte-order of the host-platform, regardless -of the byte-order of the target. In other words, it is the -responsibility of the call-back routine to convert between the -target's and the host's byte-order, if necessary. +points to is always in the byte order of the host platform, regardless +of the byte order of the target. In other words, it is the +responsibility of the callback routine to convert between the +target's and the host's byte order, if necessary. -On successful completion, the \Func{access\_reg}() call-back must +On successful completion, the \Func{access\_reg}() callback must return zero. Otherwise, the negative value of one of the -\Type{unw\_error\_t} error-codes may be returned. +\Type{unw\_error\_t} error codes may be returned. \subsection{access\_fpreg} -\Prog{Libunwind} invokes the \Func{access\_fpreg}() call-back to read +\Prog{Libunwind} invokes the \Func{access\_fpreg}() callback to read from or write to a floating-point CPU register. The index of the register to be accessed is passed in argument \Var{regnum}. To read a register, \Prog{libunwind} sets argument \Var{write} to zero and @@ -178,67 +178,67 @@ \subsection{access\_fpreg} argument \Var{write} to a non-zero value and \Var{fpvalp} to point to the variable of type \Type{unw\_fpreg\_t} that contains the value to be written. The word that \Var{fpvalp} points to is always in the -byte-order of the host-platform, regardless of the byte-order of the -target. In other words, it is the responsibility of the call-back -routine to convert between the target's and the host's byte-order, if +byte order of the host platform, regardless of the byte order of the +target. In other words, it is the responsibility of the callback +routine to convert between the target's and the host's byte order, if necessary. -On successful completion, the \Func{access\_fpreg}() call-back must +On successful completion, the \Func{access\_fpreg}() callback must return zero. Otherwise, the negative value of one of the -\Type{unw\_error\_t} error-codes may be returned. +\Type{unw\_error\_t} error codes may be returned. \subsection{resume} -\Prog{Libunwind} invokes the \Func{resume}() call-back to resume +\Prog{Libunwind} invokes the \Func{resume}() callback to resume execution in the target address space. Argument \Var{cp} is the -unwind-cursor that identifies the stack-frame in which execution +unwind cursor that identifies the stack frame in which execution should resume. By the time \Prog{libunwind} invokes the \Func{resume} -call-back, it has already established the desired machine- and -memory-state via calls to the \Func{access\_reg}(), -\Func{access\_fpreg}, and \Func{access\_mem}() call-backs. Thus, all -the call-back needs to do is perform whatever action is needed to +callback, it has already established the desired machine and +memory state via calls to the \Func{access\_reg}(), +\Func{access\_fpreg}, and \Func{access\_mem}() callbacks. Thus, all +the callback needs to do is perform whatever action is needed to actually resume execution. -The \Func{resume} call-back is invoked only in response to a call to -\Func{unw\_resume}(3), so applications which never invoke -\Func{unw\_resume}(3) need not define the \Func{resume} callback. +The \Func{resume} callback is invoked only in response to a call to +\Func{unw\_resume}(3libunwind), so applications which never invoke +\Func{unw\_resume}(3libunwind) need not define the \Func{resume} callback. -On successful completion, the \Func{resume}() call-back must return +On successful completion, the \Func{resume}() callback must return zero. Otherwise, the negative value of one of the -\Type{unw\_error\_t} error-codes may be returned. As a special case, -when resuming execution in the local address space, the call-back will +\Type{unw\_error\_t} error codes may be returned. As a special case, +when resuming execution in the local address space, the callback will not return on success. \subsection{get\_proc\_name} -\Prog{Libunwind} invokes the \Func{get\_proc\_name}() call-back to -obtain the procedure-name of a static (not dynamically generated) -procedure. Argument \Var{addr} is an instruction-address within the +\Prog{Libunwind} invokes the \Func{get\_proc\_name}() callback to +obtain the procedure name of a static (not dynamically generated) +procedure. Argument \Var{addr} is an instruction address within the procedure whose name is to be obtained. The \Var{bufp} argument is a -pointer to a character-buffer used to return the procedure name. The +pointer to a character buffer used to return the procedure name. The size of this buffer is specified in argument \Var{buf\_len}. The returned name must be terminated by a NUL character. If the procedure's name is longer than \Var{buf\_len} bytes, it must be truncated to \Var{buf\_len}\Prog{-1} bytes, with the last byte in the buffer set to the NUL character and -\Const{UNW\_ENOMEM} must be returned. Argument \Var{offp} is a pointer to a word which is used to -return the byte-offset relative to the start of the procedure whose +return the byte offset relative to the start of the procedure whose name is being returned. For example, if procedure \Func{foo}() starts at address 0x40003000, then invoking \Func{get\_proc\_name}() with \Var{addr} set to 0x40003080 should return a value of 0x80 in the word pointed to by \Var{offp} (assuming the procedure is at least 0x80 bytes long). -On successful completion, the \Func{get\_proc\_name}() call-back must +On successful completion, the \Func{get\_proc\_name}() callback must return zero. Otherwise, the negative value of one of the -\Type{unw\_error\_t} error-codes may be returned. +\Type{unw\_error\_t} error codes may be returned. \section{Return Value} On successful completion, \Func{unw\_create\_addr\_space}() returns a non-\Const{NULL} value that represents the newly created -address-space. Otherwise, \Const{NULL} is returned. +address space. Otherwise, \Const{NULL} is returned. \section{Thread and Signal Safety} @@ -247,12 +247,12 @@ \section{Thread and Signal Safety} \section{See Also} -\SeeAlso{\_U\_dyn\_register(3)}, -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_destroy\_addr\_space(3)}, -\SeeAlso{unw\_get\_proc\_info(3)}, -\SeeAlso{unw\_init\_remote(3)}, -\SeeAlso{unw\_resume(3)} +\SeeAlso{\_U\_dyn\_register}(3libunwind), +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_destroy\_addr\_space}(3libunwind), +\SeeAlso{unw\_get\_proc\_info}(3libunwind), +\SeeAlso{unw\_init\_remote}(3libunwind), +\SeeAlso{unw\_resume}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_destroy_addr_space.man b/src/native/external/libunwind/doc/unw_destroy_addr_space.man index 90c9777efbadd9..6875d40617ef27 100644 --- a/src/native/external/libunwind/doc/unw_destroy_addr_space.man +++ b/src/native/external/libunwind/doc/unw_destroy_addr_space.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:49 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_DESTROY\\_ADDR\\_SPACE" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_DESTROY\\_ADDR\\_SPACE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_destroy_addr_space \-\- destroy unwind address space @@ -43,8 +45,8 @@ undefined behavior (e.g., the application may crash). .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_destroy_addr_space.tex b/src/native/external/libunwind/doc/unw_destroy_addr_space.tex index a66b10b4b0bb4e..fcba18c9bf7211 100644 --- a/src/native/external/libunwind/doc/unw_destroy_addr_space.tex +++ b/src/native/external/libunwind/doc/unw_destroy_addr_space.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_destroy\_addr\_space}{David Mosberger-Tang}{Programming Library}{unw\_destroy\_addr\_space}unw\_destroy\_addr\_space -- destroy unwind address space +\begin{Name}{3libunwind}{unw\_destroy\_addr\_space}{David Mosberger-Tang}{Programming Library}{unw\_destroy\_addr\_space}unw\_destroy\_addr\_space -- destroy unwind address space \end{Name} \section{Synopsis} @@ -26,8 +26,8 @@ \section{Description} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_flush_cache.man b/src/native/external/libunwind/doc/unw_flush_cache.man index 627449effe2ad9..285edc448a6040 100644 --- a/src/native/external/libunwind/doc/unw_flush_cache.man +++ b/src/native/external/libunwind/doc/unw_flush_cache.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Fri Dec 2 16:09:33 PST 2016 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_FLUSH\\_CACHE" "3" "02 December 2016" "Programming Library " "Programming Library " +.TH "UNW\\_FLUSH\\_CACHE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_flush_cache \-\- flush cached info @@ -35,15 +37,15 @@ hi); .PP The unw_flush_cache() routine flushes all cached info as it -relates to address\-range lo +relates to address range lo to hi (non\-inclusive) in the -target address\-space as\&. +target address space as\&. In addition, all info cached for -address\-space as -that is not tied to a particular code\-range is +address space as +that is not tied to a particular code range is also flushed. For example, the address of the dynamic registration -list is not tied to a code\-range and its cached value (if any) is +list is not tied to a code range and its cached value (if any) is flushed by a call to this routine. The address range specified by lo and hi @@ -72,15 +74,15 @@ return a value. .PP The unw_flush_cache() -routine is thread\-safe as well as safe to +routine is thread safe as well as safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind(3), -unw_set_caching_policy(3) -unw_set_cache_size(3) +libunwind(3libunwind), +unw_set_caching_policy(3libunwind) +unw_set_cache_size(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_flush_cache.tex b/src/native/external/libunwind/doc/unw_flush_cache.tex index 32319db5d15872..a1364740c6f65b 100644 --- a/src/native/external/libunwind/doc/unw_flush_cache.tex +++ b/src/native/external/libunwind/doc/unw_flush_cache.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_flush\_cache}{David Mosberger-Tang}{Programming Library}{unw\_flush\_cache}unw\_flush\_cache -- flush cached info +\begin{Name}{3libunwind}{unw\_flush\_cache}{David Mosberger-Tang}{Programming Library}{unw\_flush\_cache}unw\_flush\_cache -- flush cached info \end{Name} \section{Synopsis} @@ -17,11 +17,11 @@ \section{Synopsis} \section{Description} The \Func{unw\_flush\_cache}() routine flushes all cached info as it -relates to address-range \Var{lo} to \Var{hi} (non-inclusive) in the -target address-space \Var{as}. In addition, all info cached for -address-space \Var{as} that is not tied to a particular code-range is +relates to address range \Var{lo} to \Var{hi} (non-inclusive) in the +target address space \Var{as}. In addition, all info cached for +address space \Var{as} that is not tied to a particular code range is also flushed. For example, the address of the dynamic registration -list is not tied to a code-range and its cached value (if any) is +list is not tied to a code range and its cached value (if any) is flushed by a call to this routine. The address range specified by \Var{lo} and \Var{hi} should be understood as a hint: \Func{unw\_flush\_cache}() may flush more information than requested, @@ -38,14 +38,14 @@ \section{Return Value} \section{Thread and Signal Safety} -The \Func{unw\_flush\_cache}() routine is thread-safe as well as safe to +The \Func{unw\_flush\_cache}() routine is thread safe as well as safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_set\_caching\_policy(3)} -\SeeAlso{unw\_set\_cache\_size(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_set\_caching\_policy}(3libunwind) +\SeeAlso{unw\_set\_cache\_size}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_accessors.man b/src/native/external/libunwind/doc/unw_get_accessors.man index 83fe4fcea6d673..7db87423592c5b 100644 --- a/src/native/external/libunwind/doc/unw_get_accessors.man +++ b/src/native/external/libunwind/doc/unw_get_accessors.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:44 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_ACCESSORS" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_ACCESSORS" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_accessors \-\- get pointer to accessor call\-backs @@ -30,18 +32,18 @@ unw_accessors_t *unw_get_accessors(unw_addr_space_t as); The unw_get_accessors() routine returns a pointer to a unw_accessors_t -structure, which contains the call\-back +structure, which contains the callback routines that were specified when address space as was created -(see unw_create_addr_space(3)). +(see unw_create_addr_space(3libunwind)). The returned pointer is guaranteed to remain valid until address space as is destroyed -by a call to unw_destroy_addr_space(3). +by a call to unw_destroy_addr_space(3libunwind). .PP Note that unw_get_accessors() can be used to retrieve the -call\-back routines for the local address space +callback routines for the local address space unw_local_addr_space\&. .PP .SH RETURN VALUE @@ -58,15 +60,15 @@ structure. .PP The unw_get_accessors() -routine is thread\-safe as well as +routine is thread safe as well as safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_destroy_addr_space(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_destroy_addr_space(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_accessors.tex b/src/native/external/libunwind/doc/unw_get_accessors.tex index bf344a32de2e61..99233329f6163f 100644 --- a/src/native/external/libunwind/doc/unw_get_accessors.tex +++ b/src/native/external/libunwind/doc/unw_get_accessors.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_accessors}{David Mosberger-Tang}{Programming Library}{unw\_get\_accessors}unw\_get\_accessors -- get pointer to accessor call-backs +\begin{Name}{3libunwind}{unw\_get\_accessors}{David Mosberger-Tang}{Programming Library}{unw\_get\_accessors}unw\_get\_accessors -- get pointer to accessor call-backs \end{Name} \section{Synopsis} @@ -17,14 +17,14 @@ \section{Synopsis} \section{Description} The \Func{unw\_get\_accessors}() routine returns a pointer to a -\Type{unw\_accessors\_t} structure, which contains the call-back +\Type{unw\_accessors\_t} structure, which contains the callback routines that were specified when address space \Var{as} was created -(see \Func{unw\_create\_addr\_space}(3)). The returned pointer is +(see \Func{unw\_create\_addr\_space}(3libunwind)). The returned pointer is guaranteed to remain valid until address space \Var{as} is destroyed -by a call to \Func{unw\_destroy\_addr\_space}(3). +by a call to \Func{unw\_destroy\_addr\_space}(3libunwind). Note that \Func{unw\_get\_accessors}() can be used to retrieve the -call-back routines for the local address space +callback routines for the local address space \Var{unw\_local\_addr\_space}. \section{Return Value} @@ -35,14 +35,14 @@ \section{Return Value} \section{Thread and Signal Safety} -The \Func{unw\_get\_accessors}() routine is thread-safe as well as +The \Func{unw\_get\_accessors}() routine is thread safe as well as safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_destroy\_addr\_space(3)} +\SeeAlso{libunwind(3libunwind)}, +\SeeAlso{unw\_create\_addr\_space(3libunwind)}, +\SeeAlso{unw\_destroy\_addr\_space(3libunwind)} \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_elf_filename.man b/src/native/external/libunwind/doc/unw_get_elf_filename.man new file mode 100644 index 00000000000000..b615f7c099b013 --- /dev/null +++ b/src/native/external/libunwind/doc/unw_get_elf_filename.man @@ -0,0 +1,110 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} +'\" t +.\" Manual page created with latex2man on Fri Sep 15 20:49:35 2023 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "UNW\\_GET\\_ELF\\_FILENAME" "3libunwind" "15 September 2023" "Programming Library " "Programming Library " +.SH NAME +unw_get_elf_filename +\-\- get backing elf filename +.PP +.SH SYNOPSIS + +.PP +#include +.br +.PP +int +unw_get_elf_filename(unw_cursor_t *cp, +char *bufp, +size_t +len, +unw_word_t *offp); +.br +.PP +.SH DESCRIPTION + +.PP +The unw_get_elf_filename() +routine returns the backing elf +filename of the current instruction pointer that created the stack frame +identified by argument cp\&. +The bufp +argument is a pointer +to a character buffer that is at least len +bytes long. This buffer +is used to return the name of the procedure. The offp +argument is +a pointer to a word that is used to return the byte offset of the instruction +pointer saved in the stack frame identified by cp, +relative to the +start of the elf file. For embedded system the symbol information may has +been stripped from a program, then unw_get_proc_name() +may be +completely unavailable, if the host side have the unstripped program with +debuginfo, then can use addr2line +command tool on host to get the +source file name and line number of the instruction pointer, with the elf +filename in bufp +and address in offp\&. +.PP +.SH RETURN VALUE + +.PP +On successful completion, unw_get_elf_filename() +returns 0. +Otherwise the negative value of one of the error codes below is +returned. +.PP +.SH THREAD AND SIGNAL SAFETY + +.PP +unw_get_elf_filename() +is thread safe. If cursor cp +is +in the local address space, this routine is also safe to use from a +signal handler. +.PP +.SH ERRORS + +.PP +.TP +UNW_EUNSPEC + An unspecified error occurred. +.TP +UNW_ENOINFO + Libunwind +was unable to determine +the elf filename of the instruction pointer. +.TP +UNW_ENOMEM + The elf filename is too long to fit +in the buffer provided. A truncated version of the name has been +returned. +.PP +.SH SEE ALSO + +.PP +libunwind(3libunwind), +unw_get_proc_info(3libunwind) +unw_get_proc_name(3libunwind) +unw_get_elf_filename_by_ip(3libunwind) +addr2line(1addr2line) +.PP +.SH AUTHOR + +.PP +Xiang Lin +.br +Email: \fBmyd.xia@gmail.com\fP +.br +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/src/native/external/libunwind/doc/unw_get_elf_filename.tex b/src/native/external/libunwind/doc/unw_get_elf_filename.tex new file mode 100644 index 00000000000000..ea6f99e80d0d8e --- /dev/null +++ b/src/native/external/libunwind/doc/unw_get_elf_filename.tex @@ -0,0 +1,71 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3libunwind}{unw\_get\_elf\_filename}{Xiang Lin}{Programming Library}{unw\_get\_elf\_filename}unw\_get\_elf\_filename -- get backing elf filename +\end{Name} + +\section{Synopsis} + +\File{\#include $<$libunwind.h$>$}\\ + +\Type{int} \Func{unw\_get\_elf\_filename}(\Type{unw\_cursor\_t~*}\Var{cp}, \Type{char~*}\Var{bufp}, \Type{size\_t} \Var{len}, \Type{unw\_word\_t~*}\Var{offp});\\ + +\section{Description} + +The \Func{unw\_get\_elf\_filename}() routine returns the backing elf +filename of the current instruction pointer that created the stack frame +identified by argument \Var{cp}. The \Var{bufp} argument is a pointer +to a character buffer that is at least \Var{len} bytes long. This buffer +is used to return the name of the procedure. The \Var{offp} argument is +a pointer to a word that is used to return the byte offset of the instruction +pointer saved in the stack frame identified by \Var{cp}, relative to the +start of the elf file. For embedded system the symbol information may has +been stripped from a program, then \Func{unw\_get\_proc\_name}() may be +completely unavailable, if the host side have the unstripped program with +debuginfo, then can use \Prog{addr2line} command tool on host to get the +source file name and line number of the instruction pointer, with the elf +filename in \Var{bufp} and address in \Var{offp}. + +\section{Return Value} + +On successful completion, \Func{unw\_get\_elf\_filename}() returns 0. +Otherwise the negative value of one of the error codes below is +returned. + +\section{Thread and Signal Safety} + +\Func{unw\_get\_elf\_filename}() is thread safe. If cursor \Var{cp} is +in the local address space, this routine is also safe to use from a +signal handler. + +\section{Errors} + +\begin{Description} +\item[\Const{UNW\_EUNSPEC}] An unspecified error occurred. +\item[\Const{UNW\_ENOINFO}] \Prog{Libunwind} was unable to determine + the elf filename of the instruction pointer. +\item[\Const{UNW\_ENOMEM}] The elf filename is too long to fit + in the buffer provided. A truncated version of the name has been + returned. +\end{Description} + +\section{See Also} + +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_get\_proc\_info}(3libunwind) +\SeeAlso{unw\_get\_proc\_name}(3libunwind) +\SeeAlso{unw\_get\_elf\_filename\_by\_ip}(3libunwind) +\SeeAlso{addr2line}(1addr2line) + +\section{Author} + +\noindent +Xiang Lin\\ +Email: \Email{myd.xia@gmail.com}\\ +\LatexManEnd + +\end{document} diff --git a/src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.man b/src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.man new file mode 100644 index 00000000000000..51b976de497ede --- /dev/null +++ b/src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.man @@ -0,0 +1,120 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} +'\" t +.\" Manual page created with latex2man on Fri Sep 15 20:49:35 2023 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "UNW\\_GET\\_ELF\\_FILENAME\\_BY\\_IP" "3libunwind" "15 September 2023" "Programming Library " "Programming Library " +.SH NAME +unw_get_elf_filename_by_ip +\-\- get backing elf filename by instruction pointer +.PP +.SH SYNOPSIS + +.PP +#include +.br +.PP +int +unw_get_elf_filename_by_ip(unw_addr_space_t as, +unw_word_t ip, +char *bufp, +size_t +len, +unw_word_t *offp, +void *arg); +.br +.PP +.SH DESCRIPTION + +.PP +The unw_get_elf_filename_by_ip() +routine returns the backing +elf filename of a instruction pointer just like unw_get_elf_filename(), +except that the name is looked up by instruction pointer (IP) instead +of a cursor. +.PP +The routine expects the following arguments: as +is the +address\-space in which the instruction pointer should be looked up. +For a look\-up in the local address\-space, +unw_local_addr_space +can be passed for this argument. +Argument ip +is the instruction\-pointer for which the elf filename +should be looked up. The bufp +argument is a pointer to +a character buffer that is at least len +bytes long. This buffer +is used to return the elf filename. The offp +argument +is a pointer to a word that is used to return the byte offset of the +instruction\-pointer relative to the start of the elf filename. +Lastly, arg +is the address space argument that should be used +when accessing the address space. It has the same purpose as the +argument of the same name for unw_init_remote(). +When +accessing the local address space (first argument is +unw_local_addr_space), +NULL +must be passed for this +argument. +.PP +.SH RETURN VALUE + +.PP +On successful completion, unw_get_elf_filename_by_ip() +returns 0. Otherwise the negative value of one of the error codes +below is returned. +.PP +.SH THREAD AND SIGNAL SAFETY + +.PP +unw_get_elf_filename_by_ip() +is thread safe. If the local +address\-space is passed in argument as, +this routine is also +safe to use from a signal handler. +.PP +.SH ERRORS + +.PP +.TP +UNW_EUNSPEC + An unspecified error occurred. +.TP +UNW_ENOINFO + Libunwind +was unable to determine +the elf filename of the instruction pointer. +.TP +UNW_ENOMEM + The elf filename is too long to fit +in the buffer provided. A truncated version of the name has been +returned. +.PP +.SH SEE ALSO + +.PP +libunwind(3libunwind), +unw_get_elf_filename(3libunwind), +unw_create_addr_space(3libunwind), +unw_init_remote(3libunwind) +.PP +.SH AUTHOR + +.PP +Xiang Lin +.br +Email: \fBmyd.xia@gmail.com\fP +.br +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.tex b/src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.tex new file mode 100644 index 00000000000000..5d625ec9a22ee8 --- /dev/null +++ b/src/native/external/libunwind/doc/unw_get_elf_filename_by_ip.tex @@ -0,0 +1,78 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3libunwind}{unw\_get\_elf\_filename\_by\_ip}{Xiang Lin}{Programming Library}{unw\_get\_elf\_filename}unw\_get\_elf\_filename\_by\_ip -- get backing elf filename by instruction pointer +\end{Name} + +\section{Synopsis} + +\File{\#include $<$libunwind.h$>$}\\ + +\Type{int} \Func{unw\_get\_elf\_filename\_by\_ip}(\Type{unw\_addr\_space\_t~}\Var{as}, \Type{unw\_word\_t~}\Var{ip}, \Type{char~*}\Var{bufp}, \Type{size\_t} \Var{len}, \Type{unw\_word\_t~*}\Var{offp}, \Type{void~*}\Var{arg});\\ + +\section{Description} + +The \Func{unw\_get\_elf\_filename\_by\_ip}() routine returns the backing +elf filename of a instruction pointer just like \Func{unw\_get\_elf\_filename}(), +except that the name is looked up by instruction pointer (IP) instead +of a cursor. + +The routine expects the following arguments: \Var{as} is the +address-space in which the instruction pointer should be looked up. +For a look-up in the local address-space, +\Var{unw\_local\_addr\_space} can be passed for this argument. +Argument \Var{ip} is the instruction-pointer for which the elf filename +should be looked up. The \Var{bufp} argument is a pointer to +a character buffer that is at least \Var{len} bytes long. This buffer +is used to return the elf filename. The \Var{offp} argument +is a pointer to a word that is used to return the byte offset of the +instruction-pointer relative to the start of the elf filename. +Lastly, \Var{arg} is the address space argument that should be used +when accessing the address space. It has the same purpose as the +argument of the same name for \Func{unw\_init\_remote}(). When +accessing the local address space (first argument is +\Var{unw\_local\_addr\_space}), \Const{NULL} must be passed for this +argument. + +\section{Return Value} + +On successful completion, \Func{unw\_get\_elf\_filename\_by\_ip}() +returns 0. Otherwise the negative value of one of the error codes +below is returned. + +\section{Thread and Signal Safety} + +\Func{unw\_get\_elf\_filename\_by\_ip}() is thread safe. If the local +address-space is passed in argument \Var{as}, this routine is also +safe to use from a signal handler. + +\section{Errors} + +\begin{Description} +\item[\Const{UNW\_EUNSPEC}] An unspecified error occurred. +\item[\Const{UNW\_ENOINFO}] \Prog{Libunwind} was unable to determine + the elf filename of the instruction pointer. +\item[\Const{UNW\_ENOMEM}] The elf filename is too long to fit + in the buffer provided. A truncated version of the name has been + returned. +\end{Description} + +\section{See Also} + +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_get\_elf\_filename}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_init\_remote}(3libunwind) + +\section{Author} + +\noindent +Xiang Lin\\ +Email: \Email{myd.xia@gmail.com}\\ +\LatexManEnd + +\end{document} diff --git a/src/native/external/libunwind/doc/unw_get_fpreg.man b/src/native/external/libunwind/doc/unw_get_fpreg.man index 5e54ca13d3bb52..02f26b7317ad79 100644 --- a/src/native/external/libunwind/doc/unw_get_fpreg.man +++ b/src/native/external/libunwind/doc/unw_get_fpreg.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:45:59 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_FPREG" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_FPREG" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_fpreg \-\- get contents of floating\-point register @@ -38,20 +40,20 @@ in the stack frame identified by cursor cp and stores the value in the variable pointed to by valp\&. .PP The register numbering is target\-dependent and described in separate -manual pages (e.g., libunwind\-ia64(3) for the IA\-64 target). +manual pages (e.g., libunwind\-ia64(3libunwind) for the IA\-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that cp is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee\-saved\&'') registers and frame\-related registers (such as the stack\-pointer). However, for signal frames (see -unw_is_signal_frame(3)), +unw_is_signal_frame(3libunwind)), it is usually possible to access all registers. .PP Note that unw_get_fpreg() can only read the contents of -floating\-point registers. See unw_get_fpreg(3) +floating\-point registers. See unw_get_fpreg(3libunwind) for a way to read registers which fit in a single word. .PP @@ -60,14 +62,14 @@ read registers which fit in a single word. .PP On successful completion, unw_get_fpreg() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_get_fpreg() -is thread\-safe as well as safe to use +is thread safe as well as safe to use from a signal handler. .PP .SH ERRORS @@ -88,17 +90,17 @@ access_reg(), and access_fpreg() call\-backs (see -unw_create_addr_space(3)). +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -libunwind\-ia64(3), -unw_get_reg(3), -unw_is_fpreg(3), -unw_is_signal_frame(3), -unw_set_fpreg(3) +libunwind(3libunwind), +libunwind\-ia64(3libunwind), +unw_get_reg(3libunwind), +unw_is_fpreg(3libunwind), +unw_is_signal_frame(3libunwind), +unw_set_fpreg(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_fpreg.tex b/src/native/external/libunwind/doc/unw_get_fpreg.tex index dd679adc06ab75..25f100bf355fa8 100644 --- a/src/native/external/libunwind/doc/unw_get_fpreg.tex +++ b/src/native/external/libunwind/doc/unw_get_fpreg.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_fpreg}{David Mosberger-Tang}{Programming Library}{unw\_get\_fpreg}unw\_get\_fpreg -- get contents of floating-point register +\begin{Name}{3libunwind}{unw\_get\_fpreg}{David Mosberger-Tang}{Programming Library}{unw\_get\_fpreg}unw\_get\_fpreg -- get contents of floating-point register \end{Name} \section{Synopsis} @@ -21,28 +21,28 @@ \section{Description} and stores the value in the variable pointed to by \Var{valp}. The register numbering is target-dependent and described in separate -manual pages (e.g., libunwind-ia64(3) for the IA-64 target). +manual pages (e.g., libunwind-ia64(3libunwind) for the IA-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that \Var{cp} is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee-saved'') registers and frame-related registers (such as the stack-pointer). However, for signal frames (see -\Func{unw\_is\_signal\_frame}(3)), it is usually possible to access +\Func{unw\_is\_signal\_frame}(3libunwind)), it is usually possible to access all registers. Note that \Func{unw\_get\_fpreg}() can only read the contents of -floating-point registers. See \Func{unw\_get\_fpreg}(3) for a way to +floating-point registers. See \Func{unw\_get\_fpreg}(3libunwind) for a way to read registers which fit in a single word. \section{Return Value} On successful completion, \Func{unw\_get\_fpreg}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_get\_fpreg}() is thread-safe as well as safe to use +\Func{unw\_get\_fpreg}() is thread safe as well as safe to use from a signal handler. \section{Errors} @@ -55,16 +55,16 @@ \section{Errors} In addition, \Func{unw\_get\_fpreg}() may return any error returned by the \Func{access\_mem}(), \Func{access\_reg}(), and \Func{access\_fpreg}() call-backs (see -\Func{unw\_create\_addr\_space}(3)). +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{libunwind-ia64(3)}, -\SeeAlso{unw\_get\_reg(3)}, -\SeeAlso{unw\_is\_fpreg(3)}, -\SeeAlso{unw\_is\_signal\_frame(3)}, -\SeeAlso{unw\_set\_fpreg(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{libunwind-ia64}(3libunwind), +\SeeAlso{unw\_get\_reg}(3libunwind), +\SeeAlso{unw\_is\_fpreg}(3libunwind), +\SeeAlso{unw\_is\_signal\_frame}(3libunwind), +\SeeAlso{unw\_set\_fpreg}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_proc_info.man b/src/native/external/libunwind/doc/unw_get_proc_info.man index 5853e1ea8e443a..047dc940dffe2e 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_info.man +++ b/src/native/external/libunwind/doc/unw_get_proc_info.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_PROC\\_INFO" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_PROC\\_INFO" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_proc_info \-\- get info on current procedure @@ -59,7 +61,7 @@ member is cleared to 0. .TP unw_word_t lsda The address of the -language\-specific data\-area (LSDA). This area normally contains +language\-specific data area (LSDA). This area normally contains language\-specific information needed during exception handling. If the procedure has no such area, this member is cleared to 0. .br @@ -73,11 +75,11 @@ member is cleared to 0. .br .TP unw_word_t gp - The global\-pointer of the + The global pointer of the procedure. On platforms that do not use a global pointer, this member may contain an undefined value. On all other platforms, it -must be set either to the correct global\-pointer value of the -procedure or to 0 if the proper global\-pointer cannot be +must be set either to the correct global pointer value of the +procedure or to 0 if the proper global pointer cannot be obtained for some reason. .br .TP @@ -86,23 +88,23 @@ unw_word_t flags currently no target\-independent flags. For the IA\-64 target, the flag UNW_PI_FLAG_IA64_RBS_SWITCH is set if the -procedure may switch the register\-backing store. +procedure may switch the register backing store. .br .TP int format - The format of the unwind\-info for this -procedure. If the unwind\-info consists of dynamic procedure info, + The format of the unwind info for this +procedure. If the unwind info consists of dynamic procedure info, format is equal to UNW_INFO_FORMAT_DYNAMIC\&. If the -unwind\-info consists of a (target\-specific) unwind table, it is +unwind info consists of a (target\-specific) unwind table, it is equal to UNW_INFO_FORMAT_TABLE\&. All other values are reserved for future use by libunwind\&. This member exists for use by the find_proc_info() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). The unw_get_proc_info() routine @@ -110,11 +112,11 @@ may return an undefined value in this member. .br .TP int unwind_info_size - The size of the unwind\-info + The size of the unwind info in bytes. This member exists for use by the find_proc_info() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). The unw_get_proc_info() routine @@ -122,13 +124,13 @@ may return an undefined value in this member. .br .TP void *unwind_info - The pointer to the unwind\-info. + The pointer to the unwind info. If no unwind info is available, this member must be set to NULL\&. This member exists for use by the find_proc_info() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). The unw_get_proc_info() routine @@ -151,16 +153,16 @@ separate procedure. .PP On successful completion, unw_get_proc_info() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_get_proc_info() -is thread\-safe. If cursor cp +is thread safe. If cursor cp is -in the local address\-space, this routine is also safe to use from a +in the local address space, this routine is also safe to use from a signal handler. .PP .SH ERRORS @@ -173,24 +175,24 @@ UNW_EUNSPEC UNW_ENOINFO Libunwind was unable to locate -unwind\-info for the procedure. +unwind info for the procedure. .TP UNW_EBADVERSION - The unwind\-info for the procedure has + The unwind info for the procedure has version or format that is not understood by libunwind\&. .PP In addition, unw_get_proc_info() may return any error returned by the access_mem() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_get_proc_name(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_get_proc_name(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_proc_info.tex b/src/native/external/libunwind/doc/unw_get_proc_info.tex index 00377e4177d312..41735fa8915565 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_info.tex +++ b/src/native/external/libunwind/doc/unw_get_proc_info.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_proc\_info}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_info}unw\_get\_proc\_info -- get info on current procedure +\begin{Name}{3libunwind}{unw\_get\_proc\_info}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_info}unw\_get\_proc\_info -- get info on current procedure \end{Name} \section{Synopsis} @@ -32,44 +32,44 @@ \section{Description} cannot be determined (e.g., due to lack of unwind information), the \Var{end\_ip} member is cleared to 0. \\ \item[\Type{unw\_word\_t} \Var{lsda}] The address of the - language-specific data-area (LSDA). This area normally contains + language-specific data area (LSDA). This area normally contains language-specific information needed during exception handling. If the procedure has no such area, this member is cleared to 0. \\ \item[\Type{unw\_word\_t} \Var{handler}] The address of the exception handler routine. This is sometimes called the \emph{personality} routine. If the procedure does not define a personality routine, the \Var{handler} member is cleared to 0. \\ -\item[\Type{unw\_word\_t} \Var{gp}] The global-pointer of the +\item[\Type{unw\_word\_t} \Var{gp}] The global pointer of the procedure. On platforms that do not use a global pointer, this member may contain an undefined value. On all other platforms, it - must be set either to the correct global-pointer value of the - procedure or to 0 if the proper global-pointer cannot be + must be set either to the correct global pointer value of the + procedure or to 0 if the proper global pointer cannot be obtained for some reason. \\ \item[\Type{unw\_word\_t} \Var{flags}] A set of flags. There are currently no target-independent flags. For the IA-64 target, the flag \Const{UNW\_PI\_FLAG\_IA64\_RBS\_SWITCH} is set if the - procedure may switch the register-backing store.\\ -\item[\Type{int} \Var{format}] The format of the unwind-info for this - procedure. If the unwind-info consists of dynamic procedure info, + procedure may switch the register backing store.\\ +\item[\Type{int} \Var{format}] The format of the unwind info for this + procedure. If the unwind info consists of dynamic procedure info, \Var{format} is equal to \Const{UNW\_INFO\_FORMAT\_DYNAMIC}. If the - unwind-info consists of a (target-specific) unwind table, it is + unwind info consists of a (target-specific) unwind table, it is equal to \Const{UNW\_INFO\_FORMAT\_TABLE}. All other values are reserved for future use by \Prog{libunwind}. This member exists - for use by the \Func{find\_proc\_info}() call-back (see - \Func{unw\_create\_addr\_space}(3)). The + for use by the \Func{find\_proc\_info}() callback (see + \Func{unw\_create\_addr\_space}(3libunwind)). The \Func{unw\_get\_proc\_info}() routine may return an undefined value in this member. \\ -\item[\Type{int} \Var{unwind\_info\_size}] The size of the unwind-info +\item[\Type{int} \Var{unwind\_info\_size}] The size of the unwind info in bytes. This member exists for use by the - \Func{find\_proc\_info}() call-back (see - \Func{unw\_create\_addr\_space}(3)). The + \Func{find\_proc\_info}() callback (see + \Func{unw\_create\_addr\_space}(3libunwind)). The \Func{unw\_get\_proc\_info}() routine may return an undefined value in this member.\\ -\item[\Type{void~*}\Var{unwind\_info}] The pointer to the unwind-info. +\item[\Type{void~*}\Var{unwind\_info}] The pointer to the unwind info. If no unwind info is available, this member must be set to \Const{NULL}. This member exists for use by the - \Func{find\_proc\_info}() call-back (see - \Func{unw\_create\_addr\_space}(3)). The + \Func{find\_proc\_info}() callback (see + \Func{unw\_create\_addr\_space}(3libunwind)). The \Func{unw\_get\_proc\_info}() routine may return an undefined value in this member.\\ \end{description} @@ -84,13 +84,13 @@ \section{Description} \section{Return Value} On successful completion, \Func{unw\_get\_proc\_info}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_get\_proc\_info}() is thread-safe. If cursor \Var{cp} is -in the local address-space, this routine is also safe to use from a +\Func{unw\_get\_proc\_info}() is thread safe. If cursor \Var{cp} is +in the local address space, this routine is also safe to use from a signal handler. \section{Errors} @@ -98,19 +98,19 @@ \section{Errors} \begin{Description} \item[\Const{UNW\_EUNSPEC}] An unspecified error occurred. \item[\Const{UNW\_ENOINFO}] \Prog{Libunwind} was unable to locate - unwind-info for the procedure. -\item[\Const{UNW\_EBADVERSION}] The unwind-info for the procedure has + unwind info for the procedure. +\item[\Const{UNW\_EBADVERSION}] The unwind info for the procedure has version or format that is not understood by \Prog{libunwind}. \end{Description} In addition, \Func{unw\_get\_proc\_info}() may return any error -returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +returned by the \Func{access\_mem}() callback (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_get\_proc\_name(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_get\_proc\_name}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.man b/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.man index 35771d0107a77a..360384c7b5fc19 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.man +++ b/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 11:45:59 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_PROC\\_INFO\\_BY\\_IP" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_PROC\\_INFO\\_BY\\_IP" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_proc_info_by_ip \-\- get procedure info by IP @@ -38,13 +40,13 @@ unw_get_proc_info(), except that the info is looked up by instruction\-pointer (IP) instead of a cursor. This is more flexible because it is possible to look up the info for an arbitrary procedure, -even if it is not part of the current call\-chain. However, since it +even if it is not part of the current call chain. However, since it is more flexible, it also tends to run slower (and often much slower) than unw_get_proc_info(). .PP The routine expects the following arguments: as is the -address\-space in which the instruction\-pointer should be looked up. +address\-space in which the instruction pointer should be looked up. For a look\-up in the local address\-space, unw_local_addr_space can be passed for this argument. @@ -55,11 +57,11 @@ is a pointer to a structure of type unw_proc_info_t which is used to return the info. Lastly, arg -is the address\-space argument that should be used -when accessing the address\-space. It has the same purpose as the +is the address space argument that should be used +when accessing the address space. It has the same purpose as the argument of the same name for unw_init_remote(). When -accessing the local address\-space (first argument is +accessing the local address space (first argument is unw_local_addr_space), NULL must be passed for this @@ -87,8 +89,8 @@ below is returned. .PP unw_get_proc_info_by_ip() -is thread\-safe. If the local -address\-space is passed in argument as, +is thread safe. If the local +address space is passed in argument as, this routine is also safe to use from a signal handler. .PP @@ -111,17 +113,17 @@ version or format that is not understood by libunwind\&. In addition, unw_get_proc_info_by_ip() may return any error returned by the access_mem() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_get_proc_name(3), -unw_get_proc_info(3), -unw_init_remote(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_get_proc_name(3libunwind), +unw_get_proc_info(3libunwind), +unw_init_remote(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.tex b/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.tex index 3c7c4af01db64c..36d8c05695477d 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.tex +++ b/src/native/external/libunwind/doc/unw_get_proc_info_by_ip.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_proc\_info\_by\_ip}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_info\_by\_ip}unw\_get\_proc\_info\_by\_ip -- get procedure info by IP +\begin{Name}{3libunwind}{unw\_get\_proc\_info\_by\_ip}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_info\_by\_ip}unw\_get\_proc\_info\_by\_ip -- get procedure info by IP \end{Name} \section{Synopsis} @@ -21,21 +21,21 @@ \section{Description} \Func{unw\_get\_proc\_info}(), except that the info is looked up by instruction-pointer (IP) instead of a cursor. This is more flexible because it is possible to look up the info for an arbitrary procedure, -even if it is not part of the current call-chain. However, since it +even if it is not part of the current call chain. However, since it is more flexible, it also tends to run slower (and often much slower) than \Func{unw\_get\_proc\_info}(). The routine expects the following arguments: \Var{as} is the -address-space in which the instruction-pointer should be looked up. +address-space in which the instruction pointer should be looked up. For a look-up in the local address-space, \Var{unw\_local\_addr\_space} can be passed for this argument. Argument \Var{ip} is the instruction-pointer for which the procedure info should be looked up and \Var{pip} is a pointer to a structure of type \Type{unw\_proc\_info\_t} which is used to return the info. -Lastly, \Var{arg} is the address-space argument that should be used -when accessing the address-space. It has the same purpose as the +Lastly, \Var{arg} is the address space argument that should be used +when accessing the address space. It has the same purpose as the argument of the same name for \Func{unw\_init\_remote}(). When -accessing the local address-space (first argument is +accessing the local address space (first argument is \Var{unw\_local\_addr\_space}), \Const{NULL} must be passed for this argument. @@ -55,8 +55,8 @@ \section{Return Value} \section{Thread and Signal Safety} -\Func{unw\_get\_proc\_info\_by\_ip}() is thread-safe. If the local -address-space is passed in argument \Var{as}, this routine is also +\Func{unw\_get\_proc\_info\_by\_ip}() is thread safe. If the local +address space is passed in argument \Var{as}, this routine is also safe to use from a signal handler. \section{Errors} @@ -69,16 +69,16 @@ \section{Errors} version or format that is not understood by \Prog{libunwind}. \end{Description} In addition, \Func{unw\_get\_proc\_info\_by\_ip}() may return any -error returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +error returned by the \Func{access\_mem}() callback (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_get\_proc\_name(3)}, -\SeeAlso{unw\_get\_proc\_info(3)}, -\SeeAlso{unw\_init\_remote(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_get\_proc\_name}(3libunwind), +\SeeAlso{unw\_get\_proc\_info}(3libunwind), +\SeeAlso{unw\_init\_remote}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_proc_info_in_range.man b/src/native/external/libunwind/doc/unw_get_proc_info_in_range.man index 6b8836607fa019..556731f8015db7 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_info_in_range.man +++ b/src/native/external/libunwind/doc/unw_get_proc_info_in_range.man @@ -1,7 +1,7 @@ .\" *********************************** start of \input{common.tex} .\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Wed Jun 29 18:53:42 2022 +.\" Manual page created with latex2man on Tue Aug 29 11:45:59 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -12,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_PROC\\_INFO\\_IN\\_RANGE" "3" "29 June 2022" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_PROC\\_INFO\\_IN\\_RANGE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_proc_info_in_range \-\- get procedure info in IP range and a frame index table @@ -42,9 +42,9 @@ routine returns the same kind of auxiliary information about a procedure as unw_get_proc_info_by_ip(), except that the info is looked up in -instruction\-pointer (IP) range and frame table instead of just at IP. This +instruction pointer (IP) range and frame table instead of just at IP. This is equally flexible because it is possible to look up the info for an arbitrary -procedure, even if it is not part of the current call\-chain. However, since it +procedure, even if it is not part of the current call chain. However, since it is more flexible, it also tends to run slower (and often much slower) than unw_get_proc_info(). .PP @@ -59,8 +59,8 @@ below is returned. .PP unw_get_proc_info_in_range() -is thread\-safe. If the local -address\-space is passed in argument as, +is thread safe. If the local +address space is passed in argument as, this routine is also safe to use from a signal handler. .PP @@ -86,17 +86,17 @@ UNW_EINVAL In addition, unw_get_proc_info_by_ip() may return any error returned by the access_mem() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_get_proc_info_in_range(3), -unw_create_addr_space(3), -unw_get_proc_name(3), -unw_get_proc_info(3) +libunwind(3libunwind), +unw_get_proc_info_in_range(3libunwind), +unw_create_addr_space(3libunwind), +unw_get_proc_name(3libunwind), +unw_get_proc_info(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_proc_info_in_range.tex b/src/native/external/libunwind/doc/unw_get_proc_info_in_range.tex index 8caaea85ad5404..59db019c482b64 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_info_in_range.tex +++ b/src/native/external/libunwind/doc/unw_get_proc_info_in_range.tex @@ -5,23 +5,23 @@ \begin{document} -\begin{Name}{3}{unw\_get\_proc\_info\_in\_range}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_info\_in\_range}unw\_get\_proc\_info\_in\_range -- get procedure info in IP range and a frame index table +\begin{Name}{3libunwind}{unw\_get\_proc\_info\_in\_range}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_info\_in\_range}unw\_get\_proc\_info\_in\_range -- get procedure info in IP range and a frame index table \end{Name} \section{Synopsis} \File{\#include $<$libunwind.h$>$}\\ -\Type{int} \Func{unw\_get\_proc\_info\_in\_range}(\Type{unw\_word\_t~}\Var{start_ip}, \Type{unw\_word\_t~}\Var{end_ip}, \Type{unw\_word\_t~}\Var{eh_frame_table}, \Type{unw\_word\_t~}\Var{eh_frame_table_len}, \Type{unw\_word\_t~}\Var{exidx_frame_table}, \Type{unw\_word\_t~}\Var{exidx_frame_table_len,}, \Type{unw\_addr\_space\_t~*}\Var{as}, \Type{void~*}\Var{arg});\\ +\Type{int} \Func{unw\_get\_proc\_info\_in\_range}(\Type{unw\_word\_t~}\Var{start\_ip}, \Type{unw\_word\_t~}\Var{end_ip}, \Type{unw\_word\_t~}\Var{eh_frame_table}, \Type{unw\_word\_t~}\Var{eh_frame_table_len}, \Type{unw\_word\_t~}\Var{exidx_frame_table}, \Type{unw\_word\_t~}\Var{exidx_frame_table_len,}, \Type{unw\_addr\_space\_t~*}\Var{as}, \Type{void~*}\Var{arg});\\ \section{Description} The \Func{unw\_get\_proc\_info\_in\_range}() routine returns the same kind of auxiliary information about a procedure as \Func{unw\_get\_proc\_info\_by\_ip}(), except that the info is looked up in -instruction-pointer (IP) range and frame table instead of just at IP. This +instruction pointer (IP) range and frame table instead of just at IP. This is equally flexible because it is possible to look up the info for an arbitrary -procedure, even if it is not part of the current call-chain. However, since it +procedure, even if it is not part of the current call chain. However, since it is more flexible, it also tends to run slower (and often much slower) than \Func{unw\_get\_proc\_info}(). @@ -33,8 +33,8 @@ \section{Return Value} \section{Thread and Signal Safety} -\Func{unw\_get\_proc\_info\_in\_range}() is thread-safe. If the local -address-space is passed in argument \Var{as}, this routine is also +\Func{unw\_get\_proc\_info\_in\_range}() is thread safe. If the local +address space is passed in argument \Var{as}, this routine is also safe to use from a signal handler. \section{Errors} @@ -48,16 +48,16 @@ \section{Errors} \item[\Const{UNW\_EINVAL}] An unsupported table encoding was specified. \end{Description} In addition, \Func{unw\_get\_proc\_info\_by\_ip}() may return any -error returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +error returned by the \Func{access\_mem}() callback (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_get\_proc\_info\_in\_range(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_get\_proc\_name(3)}, -\SeeAlso{unw\_get\_proc\_info(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_get\_proc\_info\_in\_range}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_get\_proc\_name}(3libunwind), +\SeeAlso{unw\_get\_proc\_info}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_proc_name.man b/src/native/external/libunwind/doc/unw_get_proc_name.man index 0240604823bc06..bcae61e9d60a75 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_name.man +++ b/src/native/external/libunwind/doc/unw_get_proc_name.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_PROC\\_NAME" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_PROC\\_NAME" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_proc_name \-\- get name of current procedure @@ -42,14 +44,14 @@ that is at least len bytes long. This buffer is used to return the name of the procedure. The offp argument is a pointer to a -word that is used to return the byte\-offset of the instruction\-pointer +word that is used to return the byte offset of the instruction\-pointer saved in the stack frame identified by cp, relative to the start of the procedure. For example, if procedure foo() starts at address 0x40003000, then invoking unw_get_proc_name() on a -stack frame with an instruction\-pointer value of 0x40003080 would +stack frame with an instruction pointer value of 0x40003080 would return a value of 0x80 in the word pointed to by offp (assuming the procedure is at least 0x80 bytes long). @@ -64,23 +66,23 @@ However, the offset returned through offp is always relative to the returned name, which ensures that the value (address) of the returned name plus the returned offset will always be equal to the -instruction\-pointer of the stack frame identified by cp\&. +instruction pointer of the stack frame identified by cp\&. .PP .SH RETURN VALUE .PP On successful completion, unw_get_proc_name() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_get_proc_name() -is thread\-safe. If cursor cp +is thread safe. If cursor cp is -in the local address\-space, this routine is also safe to use from a +in the local address space, this routine is also safe to use from a signal handler. .PP .SH ERRORS @@ -103,14 +105,14 @@ returned. In addition, unw_get_proc_name() may return any error returned by the access_mem() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_get_proc_info(3) +libunwind(3libunwind), +unw_get_proc_info(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_proc_name.tex b/src/native/external/libunwind/doc/unw_get_proc_name.tex index 028d6716728064..edcc357eff7087 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_name.tex +++ b/src/native/external/libunwind/doc/unw_get_proc_name.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_proc\_name}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_name}unw\_get\_proc\_name -- get name of current procedure +\begin{Name}{3libunwind}{unw\_get\_proc\_name}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_name}unw\_get\_proc\_name -- get name of current procedure \end{Name} \section{Synopsis} @@ -21,11 +21,11 @@ \section{Description} \Var{cp}. The \Var{bufp} argument is a pointer to a character buffer that is at least \Var{len} bytes long. This buffer is used to return the name of the procedure. The \Var{offp} argument is a pointer to a -word that is used to return the byte-offset of the instruction-pointer +word that is used to return the byte offset of the instruction-pointer saved in the stack frame identified by \Var{cp}, relative to the start of the procedure. For example, if procedure \Func{foo}() starts at address 0x40003000, then invoking \Func{unw\_get\_proc\_name}() on a -stack frame with an instruction-pointer value of 0x40003080 would +stack frame with an instruction pointer value of 0x40003080 would return a value of 0x80 in the word pointed to by \Var{offp} (assuming the procedure is at least 0x80 bytes long). @@ -38,18 +38,18 @@ \section{Description} However, the offset returned through \Var{offp} is always relative to the returned name, which ensures that the value (address) of the returned name plus the returned offset will always be equal to the -instruction-pointer of the stack frame identified by \Var{cp}. +instruction pointer of the stack frame identified by \Var{cp}. \section{Return Value} On successful completion, \Func{unw\_get\_proc\_name}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_get\_proc\_name}() is thread-safe. If cursor \Var{cp} is -in the local address-space, this routine is also safe to use from a +\Func{unw\_get\_proc\_name}() is thread safe. If cursor \Var{cp} is +in the local address space, this routine is also safe to use from a signal handler. \section{Errors} @@ -63,13 +63,13 @@ \section{Errors} returned. \end{Description} In addition, \Func{unw\_get\_proc\_name}() may return any error -returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +returned by the \Func{access\_mem}() callback (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_get\_proc\_info(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_get\_proc\_info}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.man b/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.man index 70b10aaead2a1b..4537f261d6b4c3 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.man +++ b/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Mon Aug 30 08:48:42 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,10 +12,10 @@ .fi .. -.TH "UNW\\_GET\\_PROC\\_NAME\\_BY\\_IP" "3" "30 August 2021" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_PROC\\_NAME\\_BY\\_IP" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_proc_name_by_ip -\-\- get procedure name +\-\- get procedure name .PP .SH SYNOPSIS @@ -35,98 +37,98 @@ void *arg); .PP The unw_get_proc_name_by_ip() -routine returns the name of +routine returns the name of a procedure just like unw_get_proc_name(), -except that the -name is looked up by instruction\-pointer (IP) instead of a cursor. +except that the +name is looked up by instruction pointer (IP) instead of a cursor. .PP The routine expects the following arguments: as -is the -address\-space in which the instruction\-pointer should be looked up. -For a look\-up in the local address\-space, +is the +address\-space in which the instruction pointer should be looked up. +For a look\-up in the local address\-space, unw_local_addr_space -can be passed for this argument. +can be passed for this argument. Argument ip -is the instruction\-pointer for which the procedure +is the instruction\-pointer for which the procedure name should be looked up. The bufp -argument is a pointer to +argument is a pointer to a character buffer that is at least len -bytes long. This buffer +bytes long. This buffer is used to return the name of the procedure. The offp -argument -is a pointer to a word that is used to return the byte\-offset of the -instruction\-pointer relative to the start of the procedure. +argument +is a pointer to a word that is used to return the byte offset of the +instruction\-pointer relative to the start of the procedure. Lastly, arg -is the address\-space argument that should be used -when accessing the address\-space. It has the same purpose as the +is the address space argument that should be used +when accessing the address space. It has the same purpose as the argument of the same name for unw_init_remote(). -When -accessing the local address\-space (first argument is +When +accessing the local address space (first argument is unw_local_addr_space), NULL -must be passed for this -argument. -.PP -Note that on some platforms there is no reliable way to distinguish -between procedure names and ordinary labels. Furthermore, if symbol -information has been stripped from a program, procedure names may be -completely unavailable or may be limited to those exported via a -dynamic symbol table. In such cases, +must be passed for this +argument. +.PP +Note that on some platforms there is no reliable way to distinguish +between procedure names and ordinary labels. Furthermore, if symbol +information has been stripped from a program, procedure names may be +completely unavailable or may be limited to those exported via a +dynamic symbol table. In such cases, unw_get_proc_name_by_ip() -may return the name of a label -or a preceding (nearby) procedure. However, the offset returned +may return the name of a label +or a preceding (nearby) procedure. However, the offset returned through offp -is always relative to the returned name, which -ensures that the value (address) of the returned name plus the -returned offset will always be equal to the instruction\-pointer +is always relative to the returned name, which +ensures that the value (address) of the returned name plus the +returned offset will always be equal to the instruction pointer ip\&. .PP .SH RETURN VALUE .PP On successful completion, unw_get_proc_name_by_ip() -returns 0. Otherwise the negative value of one of the error\-codes -below is returned. +returns 0. Otherwise the negative value of one of the error codes +below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_get_proc_name_by_ip() -is thread\-safe. If the local +is thread safe. If the local address\-space is passed in argument as, -this routine is also -safe to use from a signal handler. +this routine is also +safe to use from a signal handler. .PP .SH ERRORS .PP .TP UNW_EUNSPEC - An unspecified error occurred. + An unspecified error occurred. .TP UNW_ENOINFO Libunwind -was unable to determine -the name of the procedure. +was unable to determine +the name of the procedure. .TP UNW_ENOMEM - The procedure name is too long to fit -in the buffer provided. A truncated version of the name has been -returned. + The procedure name is too long to fit +in the buffer provided. A truncated version of the name has been +returned. .PP In addition, unw_get_proc_name_by_ip() -may return any error +may return any error returned by the access_mem() -call\-back (see -unw_create_addr_space(3)). +callback (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_get_proc_name(3), -unw_init_remote(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_get_proc_name(3libunwind), +unw_init_remote(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.tex b/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.tex index cb59768070bc19..5dae010fbe5d02 100644 --- a/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.tex +++ b/src/native/external/libunwind/doc/unw_get_proc_name_by_ip.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_proc\_name\_by\_ip}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_name}unw\_get\_proc\_name\_by\_ip -- get procedure name +\begin{Name}{3libunwind}{unw\_get\_proc\_name\_by\_ip}{David Mosberger-Tang}{Programming Library}{unw\_get\_proc\_name}unw\_get\_proc\_name\_by\_ip -- get procedure name \end{Name} \section{Synopsis} @@ -18,22 +18,22 @@ \section{Description} The \Func{unw\_get\_proc\_name\_by\_ip}() routine returns the name of a procedure just like \Func{unw\_get\_proc\_name}(), except that the -name is looked up by instruction-pointer (IP) instead of a cursor. +name is looked up by instruction pointer (IP) instead of a cursor. The routine expects the following arguments: \Var{as} is the -address-space in which the instruction-pointer should be looked up. +address-space in which the instruction pointer should be looked up. For a look-up in the local address-space, \Var{unw\_local\_addr\_space} can be passed for this argument. Argument \Var{ip} is the instruction-pointer for which the procedure name should be looked up. The \Var{bufp} argument is a pointer to a character buffer that is at least \Var{len} bytes long. This buffer is used to return the name of the procedure. The \Var{offp} argument -is a pointer to a word that is used to return the byte-offset of the +is a pointer to a word that is used to return the byte offset of the instruction-pointer relative to the start of the procedure. -Lastly, \Var{arg} is the address-space argument that should be used -when accessing the address-space. It has the same purpose as the +Lastly, \Var{arg} is the address space argument that should be used +when accessing the address space. It has the same purpose as the argument of the same name for \Func{unw\_init\_remote}(). When -accessing the local address-space (first argument is +accessing the local address space (first argument is \Var{unw\_local\_addr\_space}), \Const{NULL} must be passed for this argument. @@ -46,18 +46,18 @@ \section{Description} or a preceding (nearby) procedure. However, the offset returned through \Var{offp} is always relative to the returned name, which ensures that the value (address) of the returned name plus the -returned offset will always be equal to the instruction-pointer +returned offset will always be equal to the instruction pointer \Var{ip}. \section{Return Value} On successful completion, \Func{unw\_get\_proc\_name\_by\_ip}() -returns 0. Otherwise the negative value of one of the error-codes +returns 0. Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_get\_proc\_name\_by\_ip}() is thread-safe. If the local +\Func{unw\_get\_proc\_name\_by\_ip}() is thread safe. If the local address-space is passed in argument \Var{as}, this routine is also safe to use from a signal handler. @@ -72,15 +72,15 @@ \section{Errors} returned. \end{Description} In addition, \Func{unw\_get\_proc\_name\_by\_ip}() may return any error -returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +returned by the \Func{access\_mem}() callback (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_get\_proc\_name(3)}, -\SeeAlso{unw\_init\_remote(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_get\_proc\_name}(3libunwind), +\SeeAlso{unw\_init\_remote}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_get_reg.man b/src/native/external/libunwind/doc/unw_get_reg.man index 83e8bb43b64f4b..da092cdd8c551f 100644 --- a/src/native/external/libunwind/doc/unw_get_reg.man +++ b/src/native/external/libunwind/doc/unw_get_reg.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GET\\_REG" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_GET\\_REG" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_get_reg \-\- get register contents @@ -38,22 +40,22 @@ in the stack frame identified by cursor cp and stores the value in the word pointed to by valp\&. .PP -The register numbering is target\-dependent and described in separate -manual pages (e.g., libunwind\-ia64(3) for the IA\-64 target). +The register numbering is target dependent and described in separate +manual pages (e.g., libunwind\-ia64(3libunwind) for the IA\-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that cp is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee\-saved\&'') registers and frame\-related registers (such as the stack\-pointer). However, for signal frames (see -unw_is_signal_frame(3)), +unw_is_signal_frame(3libunwind)), it is usually possible to access all registers. .PP Note that unw_get_reg() can only read the contents of registers whose values fit in a single word. See -unw_get_fpreg(3) +unw_get_fpreg(3libunwind) for a way to read registers which do not fit this constraint. .PP @@ -62,14 +64,14 @@ this constraint. .PP On successful completion, unw_get_reg() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_get_reg() -is thread\-safe as well as safe to use +is thread safe as well as safe to use from a signal handler. .PP .SH ERRORS @@ -89,17 +91,17 @@ the access_mem(), access_reg(), and access_fpreg() -call\-backs (see -unw_create_addr_space(3)). +callbacks (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -libunwind\-ia64(3), -unw_get_fpreg(3), -unw_is_signal_frame(3), -unw_set_reg(3) +libunwind(3libunwind), +libunwind\-ia64(3libunwind), +unw_get_fpreg(3libunwind), +unw_is_signal_frame(3libunwind), +unw_set_reg(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_get_reg.tex b/src/native/external/libunwind/doc/unw_get_reg.tex index b66e8c0dbb4265..203729bac919a5 100644 --- a/src/native/external/libunwind/doc/unw_get_reg.tex +++ b/src/native/external/libunwind/doc/unw_get_reg.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_get\_reg}{David Mosberger-Tang}{Programming Library}{unw\_get\_reg}unw\_get\_reg -- get register contents +\begin{Name}{3libunwind}{unw\_get\_reg}{David Mosberger-Tang}{Programming Library}{unw\_get\_reg}unw\_get\_reg -- get register contents \end{Name} \section{Synopsis} @@ -20,30 +20,30 @@ \section{Description} \Var{reg} in the stack frame identified by cursor \Var{cp} and stores the value in the word pointed to by \Var{valp}. -The register numbering is target-dependent and described in separate -manual pages (e.g., libunwind-ia64(3) for the IA-64 target). +The register numbering is target dependent and described in separate +manual pages (e.g., libunwind-ia64(3libunwind) for the IA-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that \Var{cp} is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee-saved'') registers and frame-related registers (such as the stack-pointer). However, for signal frames (see -\Func{unw\_is\_signal\_frame}(3)), it is usually possible to access +\Func{unw\_is\_signal\_frame}(3libunwind)), it is usually possible to access all registers. Note that \Func{unw\_get\_reg}() can only read the contents of registers whose values fit in a single word. See -\Func{unw\_get\_fpreg}(3) for a way to read registers which do not fit +\Func{unw\_get\_fpreg}(3libunwind) for a way to read registers which do not fit this constraint. \section{Return Value} On successful completion, \Func{unw\_get\_reg}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_get\_reg}() is thread-safe as well as safe to use +\Func{unw\_get\_reg}() is thread safe as well as safe to use from a signal handler. \section{Errors} @@ -55,16 +55,16 @@ \section{Errors} \end{Description} In addition, \Func{unw\_get\_reg}() may return any error returned by the \Func{access\_mem}(), \Func{access\_reg}(), and -\Func{access\_fpreg}() call-backs (see -\Func{unw\_create\_addr\_space}(3)). +\Func{access\_fpreg}() callbacks (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{libunwind-ia64(3)}, -\SeeAlso{unw\_get\_fpreg(3)}, -\SeeAlso{unw\_is\_signal\_frame(3)}, -\SeeAlso{unw\_set\_reg(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{libunwind-ia64}(3libunwind), +\SeeAlso{unw\_get\_fpreg}(3libunwind), +\SeeAlso{unw\_is\_signal\_frame}(3libunwind), +\SeeAlso{unw\_set\_reg}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_getcontext.man b/src/native/external/libunwind/doc/unw_getcontext.man index 13725df9570cfa..2831402f8453cf 100644 --- a/src/native/external/libunwind/doc/unw_getcontext.man +++ b/src/native/external/libunwind/doc/unw_getcontext.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 12:09:48 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_GETCONTEXT" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_GETCONTEXT" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_getcontext \-\- get initial machine\-state @@ -31,12 +33,12 @@ unw_getcontext(unw_context_t *ucp); The unw_getcontext() routine initializes the context structure pointed to by ucp -with the machine\-state of the call\-site. The +with the machine state of the call site. The exact set of registers stored by unw_getcontext() is platform\-specific, but, in general, at least all preserved (``callee\-saved\&'') and all frame\-related registers, such as the -stack\-pointer, will be stored. +stack pointer, will be stored. .PP This routine is normally implemented as a macro and applications should not attempt to take its address. @@ -73,14 +75,14 @@ Otherwise, a value of \-1 is returned. .PP unw_getcontext() -is thread\-safe as well as safe to use +is thread safe as well as safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind(3), -unw_init_local(3) +libunwind(3libunwind), +unw_init_local(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_getcontext.tex b/src/native/external/libunwind/doc/unw_getcontext.tex index fa3eb814b3f650..b0b54f658fe18e 100644 --- a/src/native/external/libunwind/doc/unw_getcontext.tex +++ b/src/native/external/libunwind/doc/unw_getcontext.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_getcontext}{David Mosberger-Tang}{Programming Library}{unw\_getcontext}unw\_getcontext -- get initial machine-state +\begin{Name}{3libunwind}{unw\_getcontext}{David Mosberger-Tang}{Programming Library}{unw\_getcontext}unw\_getcontext -- get initial machine-state \end{Name} \section{Synopsis} @@ -17,11 +17,11 @@ \section{Synopsis} \section{Description} The \Func{unw\_getcontext}() routine initializes the context structure -pointed to by \Var{ucp} with the machine-state of the call-site. The +pointed to by \Var{ucp} with the machine state of the call site. The exact set of registers stored by \Func{unw\_getcontext}() is platform-specific, but, in general, at least all preserved (``callee-saved'') and all frame-related registers, such as the -stack-pointer, will be stored. +stack pointer, will be stored. This routine is normally implemented as a macro and applications should not attempt to take its address. @@ -44,13 +44,13 @@ \section{Return Value} \section{Thread and Signal Safety} -\Func{unw\_getcontext}() is thread-safe as well as safe to use +\Func{unw\_getcontext}() is thread safe as well as safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_init\_local(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_init\_local}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_init_local.man b/src/native/external/libunwind/doc/unw_init_local.man index 8b33c90ccfd72b..1674bf7f53af28 100644 --- a/src/native/external/libunwind/doc/unw_init_local.man +++ b/src/native/external/libunwind/doc/unw_init_local.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_INIT\\_LOCAL" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "UNW\\_INIT\\_LOCAL" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_init_local \-\- initialize cursor for local unwinding @@ -38,18 +40,18 @@ flag); The unw_init_local() routine initializes the unwind cursor pointed to by c -with the machine\-state in the context structure +with the machine state in the context structure pointed to by ctxt\&. -As such, the machine\-state pointed to by +As such, the machine state pointed to by ctxt identifies the initial stack frame at which unwinding -starts. The machine\-state is expected to be one provided by a call to +starts. The machine state is expected to be one provided by a call to unw_getcontext(); as such, the instruction pointer may point to the instruction after the last instruction of a function, and libunwind will back\-up the instruction pointer before beginning -a walk up the call stack. The machine\-state must remain valid for the +a walk up the call stack. The machine state must remain valid for the duration for which the cursor c is in use. .PP @@ -81,14 +83,14 @@ flag. .PP On successful completion, unw_init_local() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_init_local() -is thread\-safe as well as safe to use from a +is thread safe as well as safe to use from a signal handler. .PP .SH ERRORS @@ -114,8 +116,8 @@ wasn\&'t accessible. .SH SEE ALSO .PP -libunwind(3), -unw_init_remote(3) +libunwind(3libunwind), +unw_init_remote(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_init_local.tex b/src/native/external/libunwind/doc/unw_init_local.tex index aaba0ca75d4dd6..f8333b5e31c86c 100644 --- a/src/native/external/libunwind/doc/unw_init_local.tex +++ b/src/native/external/libunwind/doc/unw_init_local.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_init\_local}{David Mosberger-Tang}{Programming Library}{unw\_init\_local}unw\_init\_local -- initialize cursor for local unwinding +\begin{Name}{3libunwind}{unw\_init\_local}{David Mosberger-Tang}{Programming Library}{unw\_init\_local}unw\_init\_local -- initialize cursor for local unwinding \end{Name} \section{Synopsis} @@ -18,14 +18,14 @@ \section{Synopsis} \section{Description} The \Func{unw\_init\_local}() routine initializes the unwind cursor -pointed to by \Var{c} with the machine-state in the context structure -pointed to by \Var{ctxt}. As such, the machine-state pointed to by +pointed to by \Var{c} with the machine state in the context structure +pointed to by \Var{ctxt}. As such, the machine state pointed to by \Var{ctxt} identifies the initial stack frame at which unwinding -starts. The machine-state is expected to be one provided by a call to +starts. The machine state is expected to be one provided by a call to \Func{unw\_getcontext}(); as such, the instruction pointer may point to the instruction after the last instruction of a function, and \Prog{libunwind} will back-up the instruction pointer before beginning -a walk up the call stack. The machine-state must remain valid for the +a walk up the call stack. The machine state must remain valid for the duration for which the cursor \Var{c} is in use. The \Func{unw\_init\_local}() routine can be used only for unwinding in @@ -45,12 +45,12 @@ \section{Description} \section{Return Value} On successful completion, \Func{unw\_init\_local}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_init\_local}() is thread-safe as well as safe to use from a +\Func{unw\_init\_local}() is thread safe as well as safe to use from a signal handler. \section{Errors} @@ -67,7 +67,8 @@ \section{Errors} \section{See Also} -\SeeAlso{libunwind(3)}, \SeeAlso{unw\_init\_remote(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_init\_remote}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_init_remote.man b/src/native/external/libunwind/doc/unw_init_remote.man index 0acdac9617b75d..9bae9f6b3f20ef 100644 --- a/src/native/external/libunwind/doc/unw_init_remote.man +++ b/src/native/external/libunwind/doc/unw_init_remote.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_INIT\\_REMOTE" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_INIT\\_REMOTE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_init_remote \-\- initialize cursor for remote unwinding @@ -42,18 +44,18 @@ unw_local_addr_space address space created with unw_create_addr_space(). .PP The arg -void\-pointer tells the address space exactly what entity +opaque pointer tells the address space exactly what entity should be unwound. For example, if unw_local_addr_space is passed in as, then arg needs to be a pointer to a context structure containing the machine\-state of the initial stack frame. -However, other address\-spaces may instead expect a process\-id, a -thread\-id, or a pointer to an arbitrary structure which identifies the -stack\-frame chain to be unwound. In other words, the interpretation +However, other address spaces may instead expect a process ID, a +thread ID, or a pointer to an arbitrary structure which identifies the +stack frame chain to be unwound. In other words, the interpretation of arg -is entirely dependent on the address\-space in use; +is entirely dependent on the address space in use; libunwind never interprets the argument in any way on its own. .PP @@ -78,7 +80,7 @@ returned. .PP unw_init_remote() -is thread\-safe. If the local address\-space +is thread safe. If the local address space is passed in argument as, this routine is also safe to use from a signal handler. @@ -108,9 +110,9 @@ wasn\&'t accessible. .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_init_local(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_init_local(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_init_remote.tex b/src/native/external/libunwind/doc/unw_init_remote.tex index 9b4dc7997ae61f..bfb3367045f95b 100644 --- a/src/native/external/libunwind/doc/unw_init_remote.tex +++ b/src/native/external/libunwind/doc/unw_init_remote.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_init\_remote}{David Mosberger-Tang}{Programming Library}{unw\_init\_remote}unw\_init\_remote -- initialize cursor for remote unwinding +\begin{Name}{3libunwind}{unw\_init\_remote}{David Mosberger-Tang}{Programming Library}{unw\_init\_remote}unw\_init\_remote -- initialize cursor for remote unwinding \end{Name} \section{Synopsis} @@ -22,14 +22,14 @@ \section{Description} \Var{unw\_local\_addr\_space} (local address space) or to an arbitrary address space created with \Func{unw\_create\_addr\_space}(). -The \Var{arg} void-pointer tells the address space exactly what entity +The \Var{arg} opaque pointer tells the address space exactly what entity should be unwound. For example, if \Var{unw\_local\_addr\_space} is passed in \Var{as}, then \Var{arg} needs to be a pointer to a context structure containing the machine-state of the initial stack frame. -However, other address-spaces may instead expect a process-id, a -thread-id, or a pointer to an arbitrary structure which identifies the -stack-frame chain to be unwound. In other words, the interpretation -of \Var{arg} is entirely dependent on the address-space in use; +However, other address spaces may instead expect a process ID, a +thread ID, or a pointer to an arbitrary structure which identifies the +stack frame chain to be unwound. In other words, the interpretation +of \Var{arg} is entirely dependent on the address space in use; \Prog{libunwind} never interprets the argument in any way on its own. Note that \Func{unw\_init\_remote}() can be used to initiate unwinding @@ -46,7 +46,7 @@ \section{Return Value} \section{Thread and Signal Safety} -\Func{unw\_init\_remote}() is thread-safe. If the local address-space +\Func{unw\_init\_remote}() is thread safe. If the local address space is passed in argument \Var{as}, this routine is also safe to use from a signal handler. @@ -65,8 +65,9 @@ \section{Errors} \section{See Also} -\SeeAlso{libunwind(3)}, \SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_init\_local(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_init\_local}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_is_fpreg.man b/src/native/external/libunwind/doc/unw_is_fpreg.man index 0c26ec1bc740a2..9b42ed4ae7ce4c 100644 --- a/src/native/external/libunwind/doc/unw_is_fpreg.man +++ b/src/native/external/libunwind/doc/unw_is_fpreg.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_IS\\_FPREG" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_IS\\_FPREG" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_is_fpreg \-\- check if a register is a floating\-point register @@ -50,17 +52,17 @@ of 0. .PP unw_is_fpreg() -is thread\-safe as well as safe to use +is thread safe as well as safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind(3), -unw_get_reg(3), -unw_set_reg(3), -unw_get_fpreg(3), -unw_set_fpreg(3) +libunwind(3libunwind), +unw_get_reg(3libunwind), +unw_set_reg(3libunwind), +unw_get_fpreg(3libunwind), +unw_set_fpreg(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_is_fpreg.tex b/src/native/external/libunwind/doc/unw_is_fpreg.tex index c28cdc9be4fd23..6de950a6030d22 100644 --- a/src/native/external/libunwind/doc/unw_is_fpreg.tex +++ b/src/native/external/libunwind/doc/unw_is_fpreg.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_is\_fpreg}{David Mosberger-Tang}{Programming Library}{unw\_is\_fpreg}unw\_is\_fpreg -- check if a register is a floating-point register +\begin{Name}{3libunwind}{unw\_is\_fpreg}{David Mosberger-Tang}{Programming Library}{unw\_is\_fpreg}unw\_is\_fpreg -- check if a register is a floating-point register \end{Name} \section{Synopsis} @@ -30,16 +30,16 @@ \section{Return Value} \section{Thread and Signal Safety} -\Func{unw\_is\_fpreg}() is thread-safe as well as safe to use +\Func{unw\_is\_fpreg}() is thread safe as well as safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_get\_reg(3)}, -\SeeAlso{unw\_set\_reg(3)}, -\SeeAlso{unw\_get\_fpreg(3)}, -\SeeAlso{unw\_set\_fpreg(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_get\_reg}(3libunwind), +\SeeAlso{unw\_set\_reg}(3libunwind), +\SeeAlso{unw\_get\_fpreg}(3libunwind), +\SeeAlso{unw\_set\_fpreg}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_is_signal_frame.man b/src/native/external/libunwind/doc/unw_is_signal_frame.man index d9a7cda7b5fe1c..8971c0f7d2fb00 100644 --- a/src/native/external/libunwind/doc/unw_is_signal_frame.man +++ b/src/native/external/libunwind/doc/unw_is_signal_frame.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:06:25 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_IS\\_SIGNAL\\_FRAME" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_IS\\_SIGNAL\\_FRAME" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_is_signal_frame \-\- check if current frame is a signal frame @@ -31,12 +33,15 @@ unw_is_signal_frame(unw_cursor_t *cp); The unw_is_signal_frame() routine returns a positive value if the current frame identified by cp -is a signal frame, and a -value of 0 otherwise. For the purpose of this discussion, a signal -frame is a frame that was created in response to a potentially -asynchronous interruption. For UNIX and UNIX\-like platforms, such -frames are normally created by the kernel when delivering a signal. -In a kernel\-environment, a signal frame might, for example, correspond +is a signal frame, +also known as a signal trampoline, +and a value of 0 otherwise. +For the purpose of this discussion, +a signal frame is a frame that was created in response to a potentially +asynchronous interruption. +For UNIX and UNIX\-like platforms, +such frames are normally created by the kernel when delivering a signal. +In a kernel environment, a signal frame might, for example, correspond to a frame created in response to a device interrupt. .PP Signal frames are somewhat unusual because the asynchronous nature of @@ -49,14 +54,14 @@ that are normally treated as scratch (``caller\-saved\&'') registers. On successful completion, unw_is_signal_frame() returns a positive value if the current frame is a signal frame, or 0 if it is -not. Otherwise, a negative value of one of the error\-codes below is +not. Otherwise, a negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_is_signal_frame() -is thread\-safe as well as safe to use +is thread safe as well as safe to use from a signal handler. .PP .SH ERRORS @@ -71,11 +76,11 @@ whether or not the current frame is a signal frame. .SH SEE ALSO .PP -libunwind(3), -unw_get_reg(3), -unw_set_reg(3), -unw_get_fpreg(3), -unw_set_fpreg(3) +libunwind(3libunwind), +unw_get_reg(3libunwind), +unw_set_reg(3libunwind), +unw_get_fpreg(3libunwind), +unw_set_fpreg(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_is_signal_frame.tex b/src/native/external/libunwind/doc/unw_is_signal_frame.tex index f262e5600c1df7..61f2b722a11f0a 100644 --- a/src/native/external/libunwind/doc/unw_is_signal_frame.tex +++ b/src/native/external/libunwind/doc/unw_is_signal_frame.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_is\_signal\_frame}{David Mosberger-Tang}{Programming Library}{unw\_is\_signal\_frame}unw\_is\_signal\_frame -- check if current frame is a signal frame +\begin{Name}{3libunwind}{unw\_is\_signal\_frame}{David Mosberger-Tang}{Programming Library}{unw\_is\_signal\_frame}unw\_is\_signal\_frame -- check if current frame is a signal frame \end{Name} \section{Synopsis} @@ -17,12 +17,15 @@ \section{Synopsis} \section{Description} The \Func{unw\_is\_signal\_frame}() routine returns a positive value -if the current frame identified by \Var{cp} is a signal frame, and a -value of 0 otherwise. For the purpose of this discussion, a signal -frame is a frame that was created in response to a potentially -asynchronous interruption. For UNIX and UNIX-like platforms, such -frames are normally created by the kernel when delivering a signal. -In a kernel-environment, a signal frame might, for example, correspond +if the current frame identified by \Var{cp} is a signal frame, +also known as a signal trampoline, +and a value of 0 otherwise. +For the purpose of this discussion, +a signal frame is a frame that was created in response to a potentially +asynchronous interruption. +For UNIX and UNIX-like platforms, +such frames are normally created by the kernel when delivering a signal. +In a kernel environment, a signal frame might, for example, correspond to a frame created in response to a device interrupt. Signal frames are somewhat unusual because the asynchronous nature of @@ -33,12 +36,12 @@ \section{Return Value} On successful completion, \Func{unw\_is\_signal\_frame}() returns a positive value if the current frame is a signal frame, or 0 if it is -not. Otherwise, a negative value of one of the error-codes below is +not. Otherwise, a negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_is\_signal\_frame}() is thread-safe as well as safe to use +\Func{unw\_is\_signal\_frame}() is thread safe as well as safe to use from a signal handler. \section{Errors} @@ -50,11 +53,11 @@ \section{Errors} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_get\_reg(3)}, -\SeeAlso{unw\_set\_reg(3)}, -\SeeAlso{unw\_get\_fpreg(3)}, -\SeeAlso{unw\_set\_fpreg(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_get\_reg}(3libunwind), +\SeeAlso{unw\_set\_reg}(3libunwind), +\SeeAlso{unw\_get\_fpreg}(3libunwind), +\SeeAlso{unw\_set\_fpreg}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_reg_states_iterate.man b/src/native/external/libunwind/doc/unw_reg_states_iterate.man index 0c18cf964c1eb9..b037303c371b4a 100644 --- a/src/native/external/libunwind/doc/unw_reg_states_iterate.man +++ b/src/native/external/libunwind/doc/unw_reg_states_iterate.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Sun Aug 29 23:45:06 CEST 2021 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_REG\\_STATES\\_ITERATE" "3" "29 August 2021" "Programming Library " "Programming Library " +.TH "UNW\\_REG\\_STATES\\_ITERATE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_reg_states_iterate \-\- get register state info on current procedure @@ -87,15 +89,15 @@ On successful completion, unw_reg_states_iterate() returns 0. If the callback function returns a nonzero value, that indicates failure and the function returns immediately. Otherwise the negative -value of one of the error\-codes below is returned. +value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_reg_states_iterate() -is thread\-safe. If cursor cp +is thread safe. If cursor cp is -in the local address\-space, this routine is also safe to use from a +in the local address space, this routine is also safe to use from a signal handler. .PP .SH ERRORS @@ -118,13 +120,13 @@ In addition, unw_reg_states_iterate() may return any error returned by the access_mem() call\-back (see -unw_create_addr_space(3)). +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_apply_reg_state(3) +libunwind(3libunwind), +unw_apply_reg_state(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_reg_states_iterate.tex b/src/native/external/libunwind/doc/unw_reg_states_iterate.tex index c350b53394ab03..2fea77d613835f 100644 --- a/src/native/external/libunwind/doc/unw_reg_states_iterate.tex +++ b/src/native/external/libunwind/doc/unw_reg_states_iterate.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_reg\_states\_iterate}{David Mosberger-Tang}{Programming Library}{unw\_reg\_states\_iterate}unw\_reg\_states\_iterate -- get register state info on current procedure +\begin{Name}{3libunwind}{unw\_reg\_states\_iterate}{David Mosberger-Tang}{Programming Library}{unw\_reg\_states\_iterate}unw\_reg\_states\_iterate -- get register state info on current procedure \end{Name} \section{Synopsis} @@ -46,12 +46,12 @@ \section{Return Value} On successful completion, \Func{unw\_reg\_states\_iterate}() returns 0. If the callback function returns a nonzero value, that indicates failure and the function returns immediately. Otherwise the negative -value of one of the error-codes below is returned. +value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_reg\_states\_iterate}() is thread-safe. If cursor \Var{cp} is -in the local address-space, this routine is also safe to use from a +\Func{unw\_reg\_states\_iterate}() is thread safe. If cursor \Var{cp} is +in the local address space, this routine is also safe to use from a signal handler. \section{Errors} @@ -65,12 +65,12 @@ \section{Errors} \end{Description} In addition, \Func{unw\_reg\_states\_iterate}() may return any error returned by the \Func{access\_mem}() call-back (see -\Func{unw\_create\_addr\_space}(3)). +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_apply\_reg\_state(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_apply\_reg\_state}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_regname.man b/src/native/external/libunwind/doc/unw_regname.man index 1e3e2dbc6ea1fe..bcb733f3c6af48 100644 --- a/src/native/external/libunwind/doc/unw_regname.man +++ b/src/native/external/libunwind/doc/unw_regname.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_REGNAME" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_REGNAME" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_regname \-\- get register name @@ -49,13 +51,13 @@ string. .PP The unw_regname() -routine is thread\-safe as well as safe to +routine is thread safe as well as safe to use from a signal handler. .PP .SH SEE ALSO .PP -libunwind(3) +libunwind(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_regname.tex b/src/native/external/libunwind/doc/unw_regname.tex index 94b6434194d6f2..d215a36ad9b892 100644 --- a/src/native/external/libunwind/doc/unw_regname.tex +++ b/src/native/external/libunwind/doc/unw_regname.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_regname}{David Mosberger-Tang}{Programming Library}{unw\_regname}unw\_regname -- get register name +\begin{Name}{3libunwind}{unw\_regname}{David Mosberger-Tang}{Programming Library}{unw\_regname}unw\_regname -- get register name \end{Name} \section{Synopsis} @@ -29,12 +29,12 @@ \section{Return Value} \section{Thread and Signal Safety} -The \Func{unw\_regname}() routine is thread-safe as well as safe to +The \Func{unw\_regname}() routine is thread safe as well as safe to use from a signal handler. \section{See Also} -\SeeAlso{libunwind(3)} +\SeeAlso{libunwind}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_resume.man b/src/native/external/libunwind/doc/unw_resume.man index 1bf38327bf00ed..9f95c8c3f9330e 100644 --- a/src/native/external/libunwind/doc/unw_resume.man +++ b/src/native/external/libunwind/doc/unw_resume.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_RESUME" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_RESUME" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_resume \-\- resume execution in a particular stack frame @@ -86,7 +88,7 @@ handlers (aka, ``personality routines\&''). If a program needs this, it will have to do so on its own by obtaining the unw_proc_info_t of each unwound frame and appropriately processing its unwind handler and language\-specific data area (lsda). These steps are generally -dependent on the target\-platform and are regulated by the +dependent on the target platform and are regulated by the processor\-specific ABI (application\-binary interface). .PP .SH RETURN VALUE @@ -131,9 +133,9 @@ is not valid. .SH SEE ALSO .PP -libunwind(3), -unw_set_reg(3), -sigprocmask(2) +libunwind(3libunwind), +unw_set_reg(3libunwind), +sigprocmask(2) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_resume.tex b/src/native/external/libunwind/doc/unw_resume.tex index 38b18248ab66f2..e4f7b7ce7f45d3 100644 --- a/src/native/external/libunwind/doc/unw_resume.tex +++ b/src/native/external/libunwind/doc/unw_resume.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_resume}{David Mosberger-Tang}{Programming Library}{unw\_resume}unw\_resume -- resume execution in a particular stack frame +\begin{Name}{3libunwind}{unw\_resume}{David Mosberger-Tang}{Programming Library}{unw\_resume}unw\_resume -- resume execution in a particular stack frame \end{Name} \section{Synopsis} @@ -55,7 +55,7 @@ \section{Description} will have to do so on its own by obtaining the \Type{unw\_proc\_info\_t} of each unwound frame and appropriately processing its unwind handler and language-specific data area (lsda). These steps are generally -dependent on the target-platform and are regulated by the +dependent on the target platform and are regulated by the processor-specific ABI (application-binary interface). \section{Return Value} @@ -84,9 +84,9 @@ \section{Errors} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_set\_reg(3)}, -sigprocmask(2) +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_set\_reg}(3libunwind), +\SeeAlso{sigprocmask}(2) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_set_cache_size.man b/src/native/external/libunwind/doc/unw_set_cache_size.man index 34bbc53961450c..5a975d8efc1a93 100644 --- a/src/native/external/libunwind/doc/unw_set_cache_size.man +++ b/src/native/external/libunwind/doc/unw_set_cache_size.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Fri Jan 13 08:33:21 PST 2017 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_SET\\_CACHE\\_SIZE" "3" "13 January 2017" "Programming Library " "Programming Library " +.TH "UNW\\_SET\\_CACHE\\_SIZE" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_set_cache_size \-\- set unwind cache size @@ -72,10 +74,10 @@ established because the application is out of memory. .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_set_caching_policy(3), -unw_flush_cache(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_set_caching_policy(3libunwind), +unw_flush_cache(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_set_cache_size.tex b/src/native/external/libunwind/doc/unw_set_cache_size.tex index 1bd7e00df7c231..de23f76cf2e58b 100644 --- a/src/native/external/libunwind/doc/unw_set_cache_size.tex +++ b/src/native/external/libunwind/doc/unw_set_cache_size.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_set\_cache\_size}{Dave Watson}{Programming Library}{unw\_set\_cache\_size}unw\_set\_cache\_size -- set unwind cache size +\begin{Name}{3libunwind}{unw\_set\_cache\_size}{Dave Watson}{Programming Library}{unw\_set\_cache\_size}unw\_set\_cache\_size -- set unwind cache size \end{Name} \section{Synopsis} @@ -43,10 +43,10 @@ \section{Errors} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_set\_caching\_policy(3)}, -\SeeAlso{unw\_flush\_cache(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_set\_caching\_policy}(3libunwind), +\SeeAlso{unw\_flush\_cache}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_set_caching_policy.man b/src/native/external/libunwind/doc/unw_set_caching_policy.man index 4862ea545e5d45..64267b2a3658a2 100644 --- a/src/native/external/libunwind/doc/unw_set_caching_policy.man +++ b/src/native/external/libunwind/doc/unw_set_caching_policy.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Fri Dec 2 16:09:33 PST 2016 +.\" Manual page created with latex2man on Tue Aug 29 11:41:44 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_SET\\_CACHING\\_POLICY" "3" "02 December 2016" "Programming Library " "Programming Library " +.TH "UNW\\_SET\\_CACHING\\_POLICY" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_set_caching_policy \-\- set unwind caching policy @@ -71,7 +73,7 @@ unw_flush_cache() would have to be called (at least) for the address\-range that was covered by the shared library. .PP -For address spaces created via unw_create_addr_space(3), +For address spaces created via unw_create_addr_space(3libunwind), caching is turned off by default. For the local address space unw_local_addr_space, caching is turned on by default. @@ -103,10 +105,10 @@ established because the application is out of memory. .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3), -unw_set_cache_size(3), -unw_flush_cache(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +unw_set_cache_size(3libunwind), +unw_flush_cache(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_set_caching_policy.tex b/src/native/external/libunwind/doc/unw_set_caching_policy.tex index 3a4b07e84af0f3..abe20b11eddbeb 100644 --- a/src/native/external/libunwind/doc/unw_set_caching_policy.tex +++ b/src/native/external/libunwind/doc/unw_set_caching_policy.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_set\_caching\_policy}{David Mosberger-Tang}{Programming Library}{unw\_set\_caching\_policy}unw\_set\_caching\_policy -- set unwind caching policy +\begin{Name}{3libunwind}{unw\_set\_caching\_policy}{David Mosberger-Tang}{Programming Library}{unw\_set\_caching\_policy}unw\_set\_caching\_policy -- set unwind caching policy \end{Name} \section{Synopsis} @@ -41,7 +41,7 @@ \section{Description} \Func{unw\_flush\_cache}() would have to be called (at least) for the address-range that was covered by the shared library. -For address spaces created via \Func{unw\_create\_addr\_space}(3), +For address spaces created via \Func{unw\_create\_addr\_space}(3libunwind), caching is turned off by default. For the local address space \Func{unw\_local\_addr\_space}, caching is turned on by default. @@ -65,10 +65,10 @@ \section{Errors} \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)}, -\SeeAlso{unw\_set\_cache\_size(3)}, -\SeeAlso{unw\_flush\_cache(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{unw\_set\_cache\_size}(3libunwind), +\SeeAlso{unw\_flush\_cache}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_set_fpreg.man b/src/native/external/libunwind/doc/unw_set_fpreg.man index 6cefa54623ca2f..548feae7c4cb53 100644 --- a/src/native/external/libunwind/doc/unw_set_fpreg.man +++ b/src/native/external/libunwind/doc/unw_set_fpreg.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:06:25 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_SET\\_FPREG" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_SET\\_FPREG" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_set_fpreg \-\- set contents of floating\-point register @@ -40,20 +42,20 @@ to the value passed in val\&. .PP The register numbering is target\-dependent and described in separate -manual pages (e.g., libunwind\-ia64(3) for the IA\-64 target). +manual pages (e.g., libunwind\-ia64(3libunwind) for the IA\-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that cp is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee\-saved\&'') registers and frame\-related registers (such as the stack\-pointer). However, for signal frames (see -unw_is_signal_frame(3)), +unw_is_signal_frame(3libunwind)), it is usually possible to access all registers. .PP Note that unw_set_fpreg() can only write the contents of -floating\-point registers. See unw_set_reg(3) +floating\-point registers. See unw_set_reg(3libunwind) for a way to write registers which fit in a single word. .PP @@ -62,14 +64,14 @@ write registers which fit in a single word. .PP On successful completion, unw_set_fpreg() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY .PP unw_set_fpreg() -is thread\-safe as well as safe to use +is thread safe as well as safe to use from a signal handler. .PP .SH ERRORS @@ -93,18 +95,18 @@ the access_mem(), access_reg(), and access_fpreg() -call\-backs (see -unw_create_addr_space(3)). +callbacks (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -libunwind\-ia64(3), -unw_get_fpreg(3), -unw_is_fpreg(3), -unw_is_signal_frame(3), -unw_set_reg(3) +libunwind(3libunwind), +libunwind\-ia64(3libunwind), +unw_get_fpreg(3libunwind), +unw_is_fpreg(3libunwind), +unw_is_signal_frame(3libunwind), +unw_set_reg(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_set_fpreg.tex b/src/native/external/libunwind/doc/unw_set_fpreg.tex index aaf7fb25a761eb..36b74b73f5e3a6 100644 --- a/src/native/external/libunwind/doc/unw_set_fpreg.tex +++ b/src/native/external/libunwind/doc/unw_set_fpreg.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_set\_fpreg}{David Mosberger-Tang}{Programming Library}{unw\_set\_fpreg}unw\_set\_fpreg -- set contents of floating-point register +\begin{Name}{3libunwind}{unw\_set\_fpreg}{David Mosberger-Tang}{Programming Library}{unw\_set\_fpreg}unw\_set\_fpreg -- set contents of floating-point register \end{Name} \section{Synopsis} @@ -21,28 +21,28 @@ \section{Description} value passed in \Var{val}. The register numbering is target-dependent and described in separate -manual pages (e.g., libunwind-ia64(3) for the IA-64 target). +manual pages (e.g., libunwind-ia64(3libunwind) for the IA-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that \Var{cp} is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee-saved'') registers and frame-related registers (such as the stack-pointer). However, for signal frames (see -\Func{unw\_is\_signal\_frame}(3)), it is usually possible to access +\Func{unw\_is\_signal\_frame}(3libunwind)), it is usually possible to access all registers. Note that \Func{unw\_set\_fpreg}() can only write the contents of -floating-point registers. See \Func{unw\_set\_reg}(3) for a way to +floating-point registers. See \Func{unw\_set\_reg}(3libunwind) for a way to write registers which fit in a single word. \section{Return Value} On successful completion, \Func{unw\_set\_fpreg}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} -\Func{unw\_set\_fpreg}() is thread-safe as well as safe to use +\Func{unw\_set\_fpreg}() is thread safe as well as safe to use from a signal handler. \section{Errors} @@ -56,17 +56,17 @@ \section{Errors} \end{Description} In addition, \Func{unw\_set\_fpreg}() may return any error returned by the \Func{access\_mem}(), \Func{access\_reg}(), and -\Func{access\_fpreg}() call-backs (see -\Func{unw\_create\_addr\_space}(3)). +\Func{access\_fpreg}() callbacks (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{libunwind-ia64(3)}, -\SeeAlso{unw\_get\_fpreg(3)}, -\SeeAlso{unw\_is\_fpreg(3)}, -\SeeAlso{unw\_is\_signal\_frame(3)}, -\SeeAlso{unw\_set\_reg(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{libunwind-ia64}(3libunwind), +\SeeAlso{unw\_get\_fpreg}(3libunwind), +\SeeAlso{unw\_is\_fpreg}(3libunwind), +\SeeAlso{unw\_is\_signal\_frame}(3libunwind), +\SeeAlso{unw\_set\_reg}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_set_iterate_phdr_function.man b/src/native/external/libunwind/doc/unw_set_iterate_phdr_function.man new file mode 100644 index 00000000000000..c1f8eb2923af44 --- /dev/null +++ b/src/native/external/libunwind/doc/unw_set_iterate_phdr_function.man @@ -0,0 +1,83 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} +'\" t +.\" Manual page created with latex2man on Tue Aug 29 11:10:10 2023 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "UNW\\_SET\\_ITERATE\\_PHDR\\_FUNCTION" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " +.SH NAME +unw_set_iterate_phdr_function +\-\- set dl_iterate_phdr +implementation +.PP +.SH SYNOPSIS + +.PP +#include +.br +.PP +typedef int +(*unw_iterate_phdr_callback_t)(struct dl_phdr_info *, +size_t, +void *); +.br +typedef int +(*unw_iterate_phdr_func_t)(unw_iterate_phdr_callback_t, +void *); +.br +.PP +void +unw_set_iterate_phdr_function(unw_addr_space_t +as, +unw_iterate_phdr_func_t +function); +.br +.PP +.SH DESCRIPTION + +.PP +The unw_set_iterate_phdr_function() +routine sets the dl_iterate_phdr +implementation of address space as +to the function by argument function\&. +The function +will be called whenever libunwind +needs to iterate over the program headers of the application. +This is normally done by calling dl_iterate_phdr, +but this function is not signal safe. +With the help of a custom implementation caching and iterating over the program headers is also possible in an signal\-safe manner. +Though the burden lies on the user of libunwind\&. +.PP +.SH THREAD AND SIGNAL SAFETY + +.PP +unw_set_iterate_phdr_function() +is thread safe. If the local address space +is passed in argument as, +this routine is also safe to use from +a signal handler. +.PP +.SH SEE ALSO + +.PP +libunwind(3libunwind), +unw_create_addr_space(3libunwind), +dl_iterate_phdr(3libunwind), +.PP +.SH AUTHOR + +.PP +Bert Wesarg +.br +Email: \fBbert.wesarg@googlemail.com\fP +.br +WWW: \fBhttp://www.nongnu.org/libunwind/\fP\&. +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/src/native/external/libunwind/doc/unw_set_iterate_phdr_function.tex b/src/native/external/libunwind/doc/unw_set_iterate_phdr_function.tex new file mode 100644 index 00000000000000..beba287800ddce --- /dev/null +++ b/src/native/external/libunwind/doc/unw_set_iterate_phdr_function.tex @@ -0,0 +1,57 @@ +\documentclass{article} +\usepackage[fancyhdr,pdf]{latex2man} + +\input{common.tex} + +\begin{document} + +\begin{Name}{3libunwind}{unw\_set\_iterate\_phdr\_function}{Bert Wesarg}{Programming Library}{unw\_set\_iterate\_phdr\_function}unw\_set\_iterate\_phdr\_function -- set \Func{dl\_iterate\_phdr} implementation +\end{Name} + +\section{Synopsis} + + +\File{\#include $<$libunwind.h$>$}\\ + +\noindent +\Type{typedef~int} +\Func{(*unw\_iterate\_phdr\_callback\_t)}(\Type{struct~dl\_phdr\_info~*}, + \Type{size\_t}, \Type{void~*});\\ +\noindent +\Type{typedef~int} \Func{(*unw\_iterate\_phdr\_func\_t)}(\Type{unw\_iterate\_phdr\_callback\_t}, + \Type{void~*});\\ + +\noindent +\Type{void} \Func{unw\_set\_iterate\_phdr\_function}(\Type{unw\_addr\_space\_t} + \Var{as}, \Type{unw\_iterate\_phdr\_func\_t} \Var{function});\\ + +\section{Description} + +The \Func{unw\_set\_iterate\_phdr\_function}() routine sets the \Func{dl\_iterate\_phdr} implementation of address space \Var{as} to the function by argument \Var{function}. +The \Var{function} will be called whenever \Prog{libunwind} needs to iterate over the program headers of the application. +This is normally done by calling \Func{dl\_iterate\_phdr}, but this function is not signal safe. +With the help of a custom implementation caching and iterating over the program headers is also possible in an signal-safe manner. +Though the burden lies on the user of \Prog{libunwind}. + +\section{Thread and Signal Safety} + +\Func{unw\_set\_iterate\_phdr\_function}() is thread safe. If the local address space +is passed in argument \Var{as}, this routine is also safe to use from +a signal handler. + + +\section{See Also} + +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind), +\SeeAlso{dl\_iterate\_phdr}(3libunwind), + +\section{Author} + +\noindent +Bert Wesarg\\ +Email: \Email{bert.wesarg@googlemail.com}\\ +WWW: \URL{http://www.nongnu.org/libunwind/}. +\LatexManEnd + +\end{document} diff --git a/src/native/external/libunwind/doc/unw_set_reg.man b/src/native/external/libunwind/doc/unw_set_reg.man index 5d57045f747515..39ee2686edff38 100644 --- a/src/native/external/libunwind/doc/unw_set_reg.man +++ b/src/native/external/libunwind/doc/unw_set_reg.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 11:06:25 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_SET\\_REG" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_SET\\_REG" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_set_reg \-\- set register contents @@ -39,22 +41,22 @@ in the stack frame identified by cursor cp to the value passed in val\&. .PP -The register numbering is target\-dependent and described in separate -manual pages (e.g., libunwind\-ia64(3) for the IA\-64 target). +The register numbering is target dependent and described in separate +manual pages (e.g., libunwind\-ia64(3libunwind) for the IA\-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that cp is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee\-saved\&'') registers and frame\-related registers (such as the -stack\-pointer). However, for signal frames (see -unw_is_signal_frame(3)), +stack pointer). However, for signal frames (see +unw_is_signal_frame(3libunwind)), it is usually possible to access all registers. .PP Note that unw_set_reg() can only write the contents of registers whose values fit in a single word. See -unw_set_fpreg(3) +unw_set_fpreg(3libunwind) for a way to write registers which do not fit this constraint. .PP @@ -63,7 +65,7 @@ fit this constraint. .PP On successful completion, unw_set_reg() returns 0. -Otherwise the negative value of one of the error\-codes below is +Otherwise the negative value of one of the error codes below is returned. .PP .SH THREAD AND SIGNAL SAFETY @@ -94,17 +96,17 @@ the access_mem(), access_reg(), and access_fpreg() -call\-backs (see -unw_create_addr_space(3)). +callbacks (see +unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -libunwind\-ia64(3), -unw_get_reg(3), -unw_is_signal_frame(3), -unw_set_fpreg(3) +libunwind(3libunwind), +libunwind\-ia64(3libunwind), +unw_get_reg(3libunwind), +unw_is_signal_frame(3libunwind), +unw_set_fpreg(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_set_reg.tex b/src/native/external/libunwind/doc/unw_set_reg.tex index 2421846be59e78..47a4091fe23e16 100644 --- a/src/native/external/libunwind/doc/unw_set_reg.tex +++ b/src/native/external/libunwind/doc/unw_set_reg.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_set\_reg}{David Mosberger-Tang}{Programming Library}{unw\_set\_reg}unw\_set\_reg -- set register contents +\begin{Name}{3libunwind}{unw\_set\_reg}{David Mosberger-Tang}{Programming Library}{unw\_set\_reg}unw\_set\_reg -- set register contents \end{Name} \section{Synopsis} @@ -20,25 +20,25 @@ \section{Description} \Var{reg} in the stack frame identified by cursor \Var{cp} to the value passed in \Var{val}. -The register numbering is target-dependent and described in separate -manual pages (e.g., libunwind-ia64(3) for the IA-64 target). +The register numbering is target dependent and described in separate +manual pages (e.g., libunwind-ia64(3libunwind) for the IA-64 target). Furthermore, the exact set of accessible registers may depend on the type of frame that \Var{cp} is referring to. For ordinary stack frames, it is normally possible to access only the preserved (``callee-saved'') registers and frame-related registers (such as the -stack-pointer). However, for signal frames (see -\Func{unw\_is\_signal\_frame}(3)), it is usually possible to access +stack pointer). However, for signal frames (see +\Func{unw\_is\_signal\_frame}(3libunwind)), it is usually possible to access all registers. Note that \Func{unw\_set\_reg}() can only write the contents of registers whose values fit in a single word. See -\Func{unw\_set\_fpreg}(3) for a way to write registers which do not +\Func{unw\_set\_fpreg}(3libunwind) for a way to write registers which do not fit this constraint. \section{Return Value} On successful completion, \Func{unw\_set\_reg}() returns 0. -Otherwise the negative value of one of the error-codes below is +Otherwise the negative value of one of the error codes below is returned. \section{Thread and Signal Safety} @@ -57,16 +57,16 @@ \section{Errors} \end{Description} In addition, \Func{unw\_set\_reg}() may return any error returned by the \Func{access\_mem}(), \Func{access\_reg}(), and -\Func{access\_fpreg}() call-backs (see -\Func{unw\_create\_addr\_space}(3)). +\Func{access\_fpreg}() callbacks (see +\Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{libunwind-ia64(3)}, -\SeeAlso{unw\_get\_reg(3)}, -\SeeAlso{unw\_is\_signal\_frame(3)}, -\SeeAlso{unw\_set\_fpreg(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{libunwind-ia64}(3libunwind), +\SeeAlso{unw\_get\_reg}(3libunwind), +\SeeAlso{unw\_is\_signal\_frame}(3libunwind), +\SeeAlso{unw\_set\_fpreg}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_step.man b/src/native/external/libunwind/doc/unw_step.man index 54da1b2f3d62a1..ef1373175e12fb 100644 --- a/src/native/external/libunwind/doc/unw_step.man +++ b/src/native/external/libunwind/doc/unw_step.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Thu Aug 16 09:44:45 MDT 2007 +.\" Manual page created with latex2man on Tue Aug 29 10:53:42 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_STEP" "3" "16 August 2007" "Programming Library " "Programming Library " +.TH "UNW\\_STEP" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_step \-\- advance to next stack frame @@ -87,13 +89,13 @@ get_dyn_info_list_addr(), access_mem(), access_reg(), or access_fpreg() -call\-backs (see unw_create_addr_space(3)). +call\-backs (see unw_create_addr_space(3libunwind)). .PP .SH SEE ALSO .PP -libunwind(3), -unw_create_addr_space(3) +libunwind(3libunwind), +unw_create_addr_space(3libunwind) .PP .SH AUTHOR diff --git a/src/native/external/libunwind/doc/unw_step.tex b/src/native/external/libunwind/doc/unw_step.tex index 106bd9ba99c0c7..6180f583828a5d 100644 --- a/src/native/external/libunwind/doc/unw_step.tex +++ b/src/native/external/libunwind/doc/unw_step.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_step}{David Mosberger-Tang}{Programming Library}{unw\_step}unw\_step -- advance to next stack frame +\begin{Name}{3libunwind}{unw\_step}{David Mosberger-Tang}{Programming Library}{unw\_step}unw\_step -- advance to next stack frame \end{Name} \section{Synopsis} @@ -50,12 +50,12 @@ \section{Errors} In addition, \Func{unw\_step}() may return any error returned by the \Func{find\_proc\_info}(), \Func{get\_dyn\_info\_list\_addr}(), \Func{access\_mem}(), \Func{access\_reg}(), or \Func{access\_fpreg}() -call-backs (see \Func{unw\_create\_addr\_space}(3)). +call-backs (see \Func{unw\_create\_addr\_space}(3libunwind)). \section{See Also} -\SeeAlso{libunwind(3)}, -\SeeAlso{unw\_create\_addr\_space(3)} +\SeeAlso{libunwind}(3libunwind), +\SeeAlso{unw\_create\_addr\_space}(3libunwind) \section{Author} diff --git a/src/native/external/libunwind/doc/unw_strerror.man b/src/native/external/libunwind/doc/unw_strerror.man index 467c44d26204c3..63767c9bbc3634 100644 --- a/src/native/external/libunwind/doc/unw_strerror.man +++ b/src/native/external/libunwind/doc/unw_strerror.man @@ -1,5 +1,7 @@ +.\" *********************************** start of \input{common.tex} +.\" *********************************** end of \input{common.tex} '\" t -.\" Manual page created with latex2man on Wed Aug 18 16:51:29 CEST 2004 +.\" Manual page created with latex2man on Tue Aug 29 10:53:42 2023 .\" NOTE: This file is generated, DO NOT EDIT. .de Vb .ft CW @@ -10,7 +12,7 @@ .fi .. -.TH "UNW\\_STRERROR" "3" "18 August 2004" "Programming Library " "Programming Library " +.TH "UNW\\_STRERROR" "3libunwind" "29 August 2023" "Programming Library " "Programming Library " .SH NAME unw_strerror \-\- get text corresponding to error code @@ -53,11 +55,11 @@ from a signal handler. .PP Thomas Hallgren -.br +.br BEA Systems -.br +.br Stockholm, Sweden -.br +.br Email: \fBthallgre@bea.com\fP .br .\" NOTE: This file is generated, DO NOT EDIT. diff --git a/src/native/external/libunwind/doc/unw_strerror.tex b/src/native/external/libunwind/doc/unw_strerror.tex index 7cad011768d73d..77599ca93f3c4e 100644 --- a/src/native/external/libunwind/doc/unw_strerror.tex +++ b/src/native/external/libunwind/doc/unw_strerror.tex @@ -5,7 +5,7 @@ \begin{document} -\begin{Name}{3}{unw\_strerror}{Thomas Hallgren}{Programming Library}{unw\_strerror}unw\_strerror -- get text corresponding to error code +\begin{Name}{3libunwind}{unw\_strerror}{Thomas Hallgren}{Programming Library}{unw\_strerror}unw\_strerror -- get text corresponding to error code \end{Name} \section{Synopsis} diff --git a/src/native/external/libunwind/include/compiler.h b/src/native/external/libunwind/include/compiler.h index 22939483cddb1e..3d8519347ba081 100644 --- a/src/native/external/libunwind/include/compiler.h +++ b/src/native/external/libunwind/include/compiler.h @@ -31,6 +31,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define COMPILER_H #ifdef __GNUC__ +#ifndef __has_attribute +# define __has_attribute(x) (0) +#endif # define CONST_ATTR __attribute__((__const__)) # define UNUSED __attribute__((unused)) # define NOINLINE __attribute__((noinline)) @@ -40,9 +43,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 2) # define ALWAYS_INLINE inline __attribute__((always_inline)) # define HIDDEN __attribute__((visibility ("hidden"))) +# if __has_attribute(fallthrough) +# define FALLTHROUGH __attribute__((fallthrough)) +# else +# define FALLTHROUGH +# endif # else # define ALWAYS_INLINE # define HIDDEN +# define FALLTHROUGH # endif # define WEAK __attribute__((weak)) # if (__GNUC__ >= 3) @@ -60,6 +69,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # define NORETURN # define ALIAS(name) # define HIDDEN +# define FALLTHROUGH # define WEAK # define likely(x) (x) # define unlikely(x) (x) diff --git a/src/native/external/libunwind/include/dwarf.h b/src/native/external/libunwind/include/dwarf.h index dd9014b7177bcf..4fd1dba02d08fe 100644 --- a/src/native/external/libunwind/include/dwarf.h +++ b/src/native/external/libunwind/include/dwarf.h @@ -32,12 +32,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ struct dwarf_cursor; /* forward-declaration */ struct elf_dyn_info; -#include "dwarf-config.h" - #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include "dwarf-config.h" + #ifndef UNW_REMOTE_ONLY #if defined(HAVE_LINK_H) #include diff --git a/src/native/external/libunwind/include/dwarf_i.h b/src/native/external/libunwind/include/dwarf_i.h index 58f8034012e1c8..0f47082adbb732 100644 --- a/src/native/external/libunwind/include/dwarf_i.h +++ b/src/native/external/libunwind/include/dwarf_i.h @@ -43,8 +43,8 @@ typedef union __attribute__ ((packed)) dwarf_misaligned_value_t; static inline int -dwarf_reads8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - int8_t *val, void *arg) +dwarf_reads8 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + int8_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -54,8 +54,8 @@ dwarf_reads8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_reads16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - int16_t *val, void *arg) +dwarf_reads16 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + int16_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -65,8 +65,8 @@ dwarf_reads16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_reads32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - int32_t *val, void *arg) +dwarf_reads32 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + int32_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -76,8 +76,8 @@ dwarf_reads32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - int64_t *val, void *arg) +dwarf_reads64 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + int64_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -87,8 +87,8 @@ dwarf_reads64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_readu8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - uint8_t *val, void *arg) +dwarf_readu8 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + uint8_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -98,8 +98,8 @@ dwarf_readu8 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_readu16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - uint16_t *val, void *arg) +dwarf_readu16 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + uint16_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -109,8 +109,8 @@ dwarf_readu16 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_readu32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - uint32_t *val, void *arg) +dwarf_readu32 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + uint32_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; @@ -120,8 +120,8 @@ dwarf_readu32 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, } static inline int -dwarf_readu64 (unw_addr_space_t as, unw_accessors_t *a, unw_word_t *addr, - uint64_t *val, void *arg) +dwarf_readu64 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, unw_word_t *addr, + uint64_t *val, void *arg UNUSED) { dwarf_misaligned_value_t *mvp = (void *) (uintptr_t) *addr; diff --git a/src/native/external/libunwind/include/libunwind-aarch64.h b/src/native/external/libunwind/include/libunwind-aarch64.h index 63be0530a4e8dd..7751e4eb8be5e9 100644 --- a/src/native/external/libunwind/include/libunwind-aarch64.h +++ b/src/native/external/libunwind/include/libunwind-aarch64.h @@ -2,6 +2,7 @@ Copyright (C) 2001-2004 Hewlett-Packard Co Contributed by David Mosberger-Tang Copyright (C) 2013 Linaro Limited + Copyright 2022 Blackberry Limited This file is part of libunwind. @@ -35,6 +36,7 @@ extern "C" { #include #include #include +#include #ifndef UNW_EMPTY_STRUCT # define UNW_EMPTY_STRUCT uint8_t unused; @@ -62,6 +64,8 @@ typedef int64_t unw_sword_t; typedef long double unw_tdep_fpreg_t; +#define UNW_WORD_MAX UINT64_MAX + typedef struct { /* no aarch64-specific auxiliary proc-info */ @@ -228,9 +232,6 @@ typedef struct #else /* On AArch64, we can directly use ucontext_t as the unwind context. */ typedef ucontext_t unw_tdep_context_t; -#if defined(__FreeBSD__) -typedef ucontext_t unw_fpsimd_context_t; -#endif #endif @@ -238,9 +239,11 @@ typedef ucontext_t unw_fpsimd_context_t; #include "libunwind-dynamic.h" #if defined(__FreeBSD__) -#define UNW_BASE register uint64_t unw_base __asm__ ("x0") = (uint64_t) unw_ctx->uc_mcontext.mc_gpregs.gp_x[0]; +# define UNW_BASE register uint64_t unw_base __asm__ ("x0") = (uint64_t) unw_ctx->uc_mcontext.mc_gpregs.gp_x; +#elif defined(__QNX__) +# define UNW_BASE register uint64_t unw_base __asm__ ("x0") = (uint64_t) unw_ctx->uc_mcontext.cpu.gpr; #else -#define UNW_BASE register uint64_t unw_base __asm__ ("x0") = (uint64_t) unw_ctx->uc_mcontext.regs; +# define UNW_BASE register uint64_t unw_base __asm__ ("x0") = (uint64_t) unw_ctx->uc_mcontext.regs; #endif #define unw_tdep_getcontext(uc) ({ \ diff --git a/src/native/external/libunwind/include/libunwind-arm.h b/src/native/external/libunwind/include/libunwind-arm.h index 4ac1fd38258e68..6cfa577d67336f 100644 --- a/src/native/external/libunwind/include/libunwind-arm.h +++ b/src/native/external/libunwind/include/libunwind-arm.h @@ -31,6 +31,7 @@ extern "C" { #include #include +#include #ifndef UNW_EMPTY_STRUCT # define UNW_EMPTY_STRUCT uint8_t unused; @@ -55,6 +56,8 @@ typedef int32_t unw_sword_t; typedef long double unw_tdep_fpreg_t; +#define UNW_WORD_MAX UINT32_MAX + typedef enum { UNW_ARM_R0, @@ -271,9 +274,9 @@ unw_tdep_context_t; avoid altering any registers after unw_resume. */ #ifdef __SOFTFP__ -#define VSTMIA "nop\n" /* align return address to value stored by stmia */ +#define VSTMIA "nop\n\t" /* align return address to value stored by stmia */ #else -#define VSTMIA "vstmia %[base], {d0-d15}\n" /* this also aligns return address to value stored by stmia */ +#define VSTMIA "vstmia %[base], {d0-d15}\n\t" /* this also aligns return address to value stored by stmia */ #endif #ifndef __thumb__ @@ -282,32 +285,32 @@ unw_tdep_context_t; register unsigned long *r0 __asm__ ("r0"); \ register unsigned long *unw_base __asm__ ("r1") = unw_ctx->regs; \ __asm__ __volatile__ ( \ - "mov r0, #0\n" \ - "stmia %[base]!, {r0-r15}\n" \ + "mov r0, #0\n\t" \ + "stmia %[base]!, {r0-r15}\n\t" \ VSTMIA \ : [r0] "=r" (r0) : [base] "r" (unw_base) : "memory"); \ (int)r0; }) #else /* __thumb__ */ -#define unw_tdep_getcontext(uc) ({ \ - unw_tdep_context_t *unw_ctx = (uc); \ - register unsigned long *r0 __asm__ ("r0"); \ - register unsigned long *unw_base __asm__ ("r1") = unw_ctx->regs; \ - __asm__ __volatile__ ( \ - ".align 2\n" \ - "bx pc\n" \ - "nop\n" \ - ".code 32\n" \ - "mov r0, #0\n" \ - "stmia %[base], {r0-r14}\n" \ - "adr r0, ret%=+1\n" \ - "stmia %[base]!, {r0}\n" \ - VSTMIA \ - "orr r0, pc, #1\n" \ - "bx r0\n" \ - ".code 16\n" \ - "mov r0, #0\n" \ - "ret%=:\n" \ - : [r0] "=r" (r0), [base] "+r" (unw_base) : : "memory", "cc"); \ +#define unw_tdep_getcontext(uc) ({ \ + unw_tdep_context_t *unw_ctx = (uc); \ + register unsigned long *r0 __asm__ ("r0"); \ + register unsigned long *unw_base __asm__ ("r1") = unw_ctx->regs; \ + __asm__ __volatile__ ( \ + ".align 2\n\t" \ + "bx pc\n\t" \ + "nop\n\t" \ + ".code 32\n\t" \ + "mov r0, #0\n\t" \ + "stmia %[base]!, {r0-r14}\n\t" \ + "adr r0, ret%=+1\n\t" \ + "stmia %[base]!, {r0}\n\t" \ + VSTMIA \ + "orr r0, pc, #1\n\t" \ + "bx r0\n\t" \ + ".code 16\n\t" \ + "mov r0, #0\n\t" \ + "ret%=:\n" \ + : [r0] "=r" (r0), [base] "+r" (unw_base) : : "memory", "cc"); \ (int)r0; }) #endif diff --git a/src/native/external/libunwind/include/libunwind-common.h.in b/src/native/external/libunwind/include/libunwind-common.h.in index 7360a0284b25dd..893fdd69916ddb 100644 --- a/src/native/external/libunwind/include/libunwind-common.h.in +++ b/src/native/external/libunwind/include/libunwind-common.h.in @@ -208,6 +208,18 @@ typedef struct unw_accessors int (*get_proc_name) (unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); + /* Optional call back to obtain the name of a elf file where the ip belongs to. + This callback is optional and may be set to NULL. */ + int (*get_elf_filename) (unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); + + /* Optional call back to obtain the start and end ip of a procedure. + * procedure ip range is [start, end), the range is without end. + * This callback is optional and may be set to NULL. + */ + int (*get_proc_ip_range) (unw_addr_space_t, unw_word_t, unw_word_t *, + unw_word_t *, void *); + /* Optional call back to return a mask to be used with pointer * authentication on arm64. * @@ -249,6 +261,10 @@ typedef struct unw_save_loc } unw_save_loc_t; +struct dl_phdr_info; +typedef int (*unw_iterate_phdr_callback_t) (struct dl_phdr_info *, size_t, void *); +typedef int (*unw_iterate_phdr_func_t) (unw_iterate_phdr_callback_t, void *); + /* These routines work both for local and remote unwinding. */ #define unw_local_addr_space UNW_OBJ(local_addr_space) @@ -274,8 +290,11 @@ unw_save_loc_t; #define unw_is_signal_frame UNW_OBJ(is_signal_frame) #define unw_get_proc_name UNW_OBJ(get_proc_name) #define unw_get_proc_name_by_ip UNW_OBJ(get_proc_name_by_ip) +#define unw_get_elf_filename UNW_OBJ(get_elf_filename) +#define unw_get_elf_filename_by_ip UNW_OBJ(get_elf_filename_by_ip) #define unw_set_caching_policy UNW_OBJ(set_caching_policy) #define unw_set_cache_size UNW_OBJ(set_cache_size) +#define unw_set_iterate_phdr_function UNW_OBJ(set_iterate_phdr_function) #define unw_regname UNW_ARCH_OBJ(regname) #define unw_flush_cache UNW_ARCH_OBJ(flush_cache) #define unw_strerror UNW_ARCH_OBJ(strerror) @@ -287,6 +306,7 @@ extern unw_accessors_t *unw_get_accessors_int (unw_addr_space_t); extern void unw_flush_cache (unw_addr_space_t, unw_word_t, unw_word_t); extern int unw_set_caching_policy (unw_addr_space_t, unw_caching_policy_t); extern int unw_set_cache_size (unw_addr_space_t, size_t, int); +extern void unw_set_iterate_phdr_function (unw_addr_space_t, unw_iterate_phdr_func_t); extern const char *unw_regname (unw_regnum_t); extern int unw_init_local (unw_cursor_t *, unw_context_t *); @@ -314,6 +334,9 @@ extern int unw_is_signal_frame (unw_cursor_t *); extern int unw_get_proc_name (unw_cursor_t *, char *, size_t, unw_word_t *); extern int unw_get_proc_name_by_ip (unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); +extern int unw_get_elf_filename (unw_cursor_t *, char *, size_t, unw_word_t *); +extern int unw_get_elf_filename_by_ip (unw_addr_space_t, unw_word_t, char *, + size_t, unw_word_t *, void *); extern const char *unw_strerror (int); extern int unw_backtrace (void **, int); extern int unw_backtrace2 (void **, int, unw_context_t*, int); diff --git a/src/native/external/libunwind/include/libunwind-coredump.h b/src/native/external/libunwind/include/libunwind-coredump.h index 3c7814140f940d..83a974af484c12 100644 --- a/src/native/external/libunwind/include/libunwind-coredump.h +++ b/src/native/external/libunwind/include/libunwind-coredump.h @@ -62,6 +62,8 @@ extern int _UCD_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, void *); extern int _UCD_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); +extern int _UCD_get_elf_filename (unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); extern int _UCD_resume (unw_addr_space_t, unw_cursor_t *, void *); extern unw_accessors_t _UCD_accessors; diff --git a/src/native/external/libunwind/include/libunwind-hppa.h b/src/native/external/libunwind/include/libunwind-hppa.h index 7013aa772682db..ad592db85b20c5 100644 --- a/src/native/external/libunwind/include/libunwind-hppa.h +++ b/src/native/external/libunwind/include/libunwind-hppa.h @@ -30,8 +30,13 @@ extern "C" { #endif #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET hppa #define UNW_TARGET_HPPA 1 @@ -47,6 +52,8 @@ extern "C" { typedef uint32_t unw_word_t; typedef int32_t unw_sword_t; +#define UNW_WORD_MAX UINT32_MAX + typedef union { struct { unw_word_t bits[2]; } raw; @@ -97,6 +104,7 @@ hppa_regnum_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -110,6 +118,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no PA-RISC-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-ia64.h b/src/native/external/libunwind/include/libunwind-ia64.h index 0cc4f39eaab1e4..49741f0e35833a 100644 --- a/src/native/external/libunwind/include/libunwind-ia64.h +++ b/src/native/external/libunwind/include/libunwind-ia64.h @@ -27,8 +27,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define LIBUNWIND_H #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif @@ -69,6 +74,8 @@ extern "C" { typedef uint64_t unw_word_t; typedef int64_t unw_sword_t; +#define UNW_WORD_MAX UINT64_MAX + /* On IA-64, we want to access the contents of floating-point registers as a pair of "words", but to ensure 16-byte alignment, we make it a union that contains a "long double". This will do the @@ -83,6 +90,7 @@ unw_tdep_fpreg_t; typedef struct { /* no ia64-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-loongarch64.h b/src/native/external/libunwind/include/libunwind-loongarch64.h index 1e9366ed3ed2b6..7f768cd89e000e 100644 --- a/src/native/external/libunwind/include/libunwind-loongarch64.h +++ b/src/native/external/libunwind/include/libunwind-loongarch64.h @@ -32,8 +32,13 @@ extern "C" { #endif #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET loongarch64 #define UNW_TARGET_LOONGARCH64 1 @@ -53,6 +58,8 @@ typedef int64_t unw_sword_t; typedef long double unw_tdep_fpreg_t; +#define UNW_WORD_MAX UINT64_MAX + typedef enum { UNW_LOONGARCH64_R0, @@ -109,7 +116,7 @@ loongarch64_regnum_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -119,7 +126,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no loongarch64-specific auxiliary proc-info */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-mips.h b/src/native/external/libunwind/include/libunwind-mips.h index d1cc1b7e280a2b..9cb5051eea3501 100644 --- a/src/native/external/libunwind/include/libunwind-mips.h +++ b/src/native/external/libunwind/include/libunwind-mips.h @@ -30,12 +30,17 @@ extern "C" { #endif #include +#include #include #ifdef mips # undef mips #endif +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET mips #define UNW_TARGET_MIPS 1 @@ -55,8 +60,10 @@ extern "C" { ABIs, and 64-bit wide for N64 ABI. */ #if _MIPS_SIM == _ABI64 typedef uint64_t unw_word_t; +# define UNW_WORD_MAX UINT64_MAX #else typedef uint32_t unw_word_t; +# define UNW_WORD_MAX UINT32_MAX #endif typedef int32_t unw_sword_t; @@ -127,6 +134,7 @@ mips_abi_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -139,6 +147,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no mips-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-nto.h b/src/native/external/libunwind/include/libunwind-nto.h new file mode 100644 index 00000000000000..50d72c65a203d7 --- /dev/null +++ b/src/native/external/libunwind/include/libunwind-nto.h @@ -0,0 +1,69 @@ +/** + * Public interface for the QNX Neutrino remote unwinding library. + * + * This library provides helper routines to make it possible to use libunwind + * via the QNX Neutrino procfs. + */ +/* + * Copyright 2020, 2022 QNX Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef LIBUNWIND_NTO_H +#define LIBUNWIND_NTO_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +static const pthread_t LUT_ALL_THREADS = -1; + +/** + * Helper routines to make it easy to unwind using devctl() on unw_nto. + */ +extern void *unw_nto_create(pid_t, pthread_t); +extern void unw_nto_destroy(void *); + +extern int unw_nto_find_proc_info(unw_addr_space_t, unw_word_t, unw_proc_info_t *, int, void *); +extern void unw_nto_put_unwind_info(unw_addr_space_t, unw_proc_info_t *, void *); +extern int unw_nto_get_dyn_info_list_addr(unw_addr_space_t, unw_word_t *, void *); +extern int unw_nto_access_mem(unw_addr_space_t, unw_word_t, unw_word_t *, int, void *); +extern int unw_nto_access_reg(unw_addr_space_t, unw_regnum_t, unw_word_t *, int, void *); +extern int unw_nto_access_fpreg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, void *); +extern int unw_nto_get_proc_name(unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); +extern int unw_nto_get_proc_ip_range (unw_addr_space_t as, unw_word_t ip, unw_word_t *start, unw_word_t *end, void *); +extern int unw_nto_get_elf_filename(unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); +extern int unw_nto_resume(unw_addr_space_t, unw_cursor_t *, void *); + +/** + * A handy pre-defined accessor with all of the above. + */ +extern unw_accessors_t unw_nto_accessors; + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* LIBUNWIND_NTO_H */ diff --git a/src/native/external/libunwind/include/libunwind-ppc32.h b/src/native/external/libunwind/include/libunwind-ppc32.h index 47ebfde5a3cf3a..303597c02d9307 100644 --- a/src/native/external/libunwind/include/libunwind-ppc32.h +++ b/src/native/external/libunwind/include/libunwind-ppc32.h @@ -37,8 +37,13 @@ extern "C" { #endif #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET ppc32 #define UNW_TARGET_PPC32 1 @@ -67,9 +72,11 @@ extern "C" { #if __WORDSIZE==32 typedef uint32_t unw_word_t; typedef int32_t unw_sword_t; +# define UNW_WORD_MAX UINT32_MAX #else typedef uint64_t unw_word_t; typedef int64_t unw_sword_t; +# define UNW_WORD_MAX UINT64_MAX #endif typedef long double unw_tdep_fpreg_t; @@ -175,6 +182,7 @@ ppc32_regnum_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -192,6 +200,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no ppc32-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-ppc64.h b/src/native/external/libunwind/include/libunwind-ppc64.h index fed478bedcd715..1ce48bf7e95ccb 100644 --- a/src/native/external/libunwind/include/libunwind-ppc64.h +++ b/src/native/external/libunwind/include/libunwind-ppc64.h @@ -37,8 +37,13 @@ extern "C" { #endif #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET ppc64 #define UNW_TARGET_PPC64 1 @@ -67,9 +72,11 @@ extern "C" { #if __WORDSIZE==32 typedef uint32_t unw_word_t; typedef int32_t unw_sword_t; +#define UNW_WORD_MAX UINT32_MAX #else typedef uint64_t unw_word_t; typedef int64_t unw_sword_t; +#define UNW_WORD_MAX UINT64_MAX #endif typedef double unw_tdep_fpreg_t; @@ -239,6 +246,7 @@ ppc64_abi_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -256,6 +264,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no ppc64-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-ptrace.h b/src/native/external/libunwind/include/libunwind-ptrace.h index 916dbd246bf5b4..419f60efe91f35 100644 --- a/src/native/external/libunwind/include/libunwind-ptrace.h +++ b/src/native/external/libunwind/include/libunwind-ptrace.h @@ -53,6 +53,8 @@ extern int _UPT_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *, int, void *); extern int _UPT_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); +extern int _UPT_get_elf_filename (unw_addr_space_t, unw_word_t, char *, size_t, + unw_word_t *, void *); extern int _UPT_resume (unw_addr_space_t, unw_cursor_t *, void *); extern unw_accessors_t _UPT_accessors; diff --git a/src/native/external/libunwind/include/libunwind-riscv.h b/src/native/external/libunwind/include/libunwind-riscv.h index e74db0f93183aa..55605fe779a600 100644 --- a/src/native/external/libunwind/include/libunwind-riscv.h +++ b/src/native/external/libunwind/include/libunwind-riscv.h @@ -34,8 +34,13 @@ extern "C" { #include #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET riscv #define UNW_TARGET_RISCV 1 @@ -52,9 +57,11 @@ extern "C" { #if __riscv_xlen == 32 typedef uint32_t unw_word_t; typedef int32_t unw_sword_t; +# define UNW_WORD_MAX UINT32_MAX #elif __riscv_xlen == 64 typedef uint64_t unw_word_t; typedef int64_t unw_sword_t; +# define UNW_WORD_MAX UINT64_MAX #endif #if __riscv_flen == 64 @@ -157,7 +164,7 @@ riscv_regnum_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -167,7 +174,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no riscv-specific auxiliary proc-info */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-s390x.h b/src/native/external/libunwind/include/libunwind-s390x.h index ebda40d57af9b6..7d38a747b6b03e 100644 --- a/src/native/external/libunwind/include/libunwind-s390x.h +++ b/src/native/external/libunwind/include/libunwind-s390x.h @@ -36,6 +36,10 @@ extern "C" { #include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET s390x #define UNW_TARGET_S390X 1 @@ -53,6 +57,8 @@ typedef int64_t unw_sword_t; typedef double unw_tdep_fpreg_t; +#define UNW_WORD_MAX UINT64_MAX + typedef enum { /* general purpose registers */ @@ -114,7 +120,7 @@ s390x_regnum_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -124,7 +130,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no s390x-specific auxiliary proc-info */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-sh.h b/src/native/external/libunwind/include/libunwind-sh.h index 927f61ff42bdad..2218c6c602c683 100644 --- a/src/native/external/libunwind/include/libunwind-sh.h +++ b/src/native/external/libunwind/include/libunwind-sh.h @@ -32,8 +32,13 @@ extern "C" { #include #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET sh #define UNW_TARGET_SH 1 @@ -52,6 +57,8 @@ typedef int32_t unw_sword_t; typedef long double unw_tdep_fpreg_t; +#define UNW_WORD_MAX UINT32_MAX + typedef enum { UNW_SH_R0, @@ -91,6 +98,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -99,6 +107,7 @@ unw_tdep_save_loc_t; typedef struct { /* no sh-specific auxiliary proc-info */ + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind-tilegx.h b/src/native/external/libunwind/include/libunwind-tilegx.h deleted file mode 100644 index 0f84ea65ee3657..00000000000000 --- a/src/native/external/libunwind/include/libunwind-tilegx.h +++ /dev/null @@ -1,161 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef LIBUNWIND_H -#define LIBUNWIND_H - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#include -#include - -#define UNW_TARGET tilegx -#define UNW_TARGET_TILEGX 1 - -#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */ - -/* This needs to be big enough to accommodate "struct cursor", while - leaving some slack for future expansion. Changing this value will - require recompiling all users of this library. Stack allocation is - relatively cheap and unwind-state copying is relatively rare, so we - want to err on making it rather too big than too small. */ - -#define UNW_TDEP_CURSOR_LEN 4096 - -/* The size of a "word" varies on TILEGX. This type is used for memory - addresses and register values. */ -typedef uint64_t unw_word_t; -typedef int64_t unw_sword_t; - -typedef long double unw_tdep_fpreg_t; - -typedef enum -{ - UNW_TILEGX_R0, - UNW_TILEGX_R1, - UNW_TILEGX_R2, - UNW_TILEGX_R3, - UNW_TILEGX_R4, - UNW_TILEGX_R5, - UNW_TILEGX_R6, - UNW_TILEGX_R7, - UNW_TILEGX_R8, - UNW_TILEGX_R9, - UNW_TILEGX_R10, - UNW_TILEGX_R11, - UNW_TILEGX_R12, - UNW_TILEGX_R13, - UNW_TILEGX_R14, - UNW_TILEGX_R15, - UNW_TILEGX_R16, - UNW_TILEGX_R17, - UNW_TILEGX_R18, - UNW_TILEGX_R19, - UNW_TILEGX_R20, - UNW_TILEGX_R21, - UNW_TILEGX_R22, - UNW_TILEGX_R23, - UNW_TILEGX_R24, - UNW_TILEGX_R25, - UNW_TILEGX_R26, - UNW_TILEGX_R27, - UNW_TILEGX_R28, - UNW_TILEGX_R29, - UNW_TILEGX_R30, - UNW_TILEGX_R31, - UNW_TILEGX_R32, - UNW_TILEGX_R33, - UNW_TILEGX_R34, - UNW_TILEGX_R35, - UNW_TILEGX_R36, - UNW_TILEGX_R37, - UNW_TILEGX_R38, - UNW_TILEGX_R39, - UNW_TILEGX_R40, - UNW_TILEGX_R41, - UNW_TILEGX_R42, - UNW_TILEGX_R43, - UNW_TILEGX_R44, - UNW_TILEGX_R45, - UNW_TILEGX_R46, - UNW_TILEGX_R47, - UNW_TILEGX_R48, - UNW_TILEGX_R49, - UNW_TILEGX_R50, - UNW_TILEGX_R51, - UNW_TILEGX_R52, - UNW_TILEGX_R53, - UNW_TILEGX_R54, - UNW_TILEGX_R55, - - /* FIXME: Other registers! */ - - UNW_TILEGX_PC, - /* For TILEGX, the CFA is the value of SP (r54) at the call site in the - previous frame. */ - UNW_TILEGX_CFA, - - UNW_TDEP_LAST_REG = UNW_TILEGX_PC, - - UNW_TDEP_IP = UNW_TILEGX_R55, /* R55 is link register for Tilegx */ - UNW_TDEP_SP = UNW_TILEGX_R54, - UNW_TDEP_EH = UNW_TILEGX_R0 /* FIXME. */ -} tilegx_regnum_t; - -typedef enum -{ - UNW_TILEGX_ABI_N64 = 2 -} tilegx_abi_t; - -#define UNW_TDEP_NUM_EH_REGS 2 /* FIXME for TILEGX. */ - -typedef struct unw_tdep_save_loc -{ - /* Additional target-dependent info on a save location. */ -} unw_tdep_save_loc_t; - -typedef ucontext_t unw_tdep_context_t; - -#include "libunwind-dynamic.h" - -typedef struct -{ - /* no tilegx-specific auxiliary proc-info */ -} unw_tdep_proc_info_t; - -#include "libunwind-common.h" - -#define unw_tdep_getcontext getcontext - -#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg) -extern int unw_tdep_is_fpreg (int); - -#if defined(__cplusplus) || defined(c_plusplus) -} -#endif - -#endif /* LIBUNWIND_H */ diff --git a/src/native/external/libunwind/include/libunwind-x86.h b/src/native/external/libunwind/include/libunwind-x86.h index d3b741d3f3f9f0..cd42c60e49496d 100644 --- a/src/native/external/libunwind/include/libunwind-x86.h +++ b/src/native/external/libunwind/include/libunwind-x86.h @@ -32,6 +32,7 @@ extern "C" { #include #include +#include #include #ifndef UNW_EMPTY_STRUCT @@ -53,6 +54,8 @@ extern "C" { typedef uint32_t unw_word_t; typedef int32_t unw_sword_t; +#define UNW_WORD_MAX UINT32_MAX + typedef union { struct { uint8_t b[4]; } val32; struct { uint8_t b[10]; } val80; diff --git a/src/native/external/libunwind/include/libunwind-x86_64.h b/src/native/external/libunwind/include/libunwind-x86_64.h index 78eb541afb303c..a2fea33b3ba313 100644 --- a/src/native/external/libunwind/include/libunwind-x86_64.h +++ b/src/native/external/libunwind/include/libunwind-x86_64.h @@ -34,8 +34,13 @@ extern "C" { #include #include +#include #include +#ifndef UNW_EMPTY_STRUCT +# define UNW_EMPTY_STRUCT uint8_t unused; +#endif + #define UNW_TARGET x86_64 #define UNW_TARGET_X86_64 1 @@ -53,6 +58,8 @@ typedef int64_t unw_sword_t; typedef long double unw_tdep_fpreg_t; +#define UNW_WORD_MAX UINT64_MAX + typedef enum { UNW_X86_64_RAX, @@ -111,7 +118,7 @@ x86_64_regnum_t; typedef struct unw_tdep_save_loc { /* Additional target-dependent info on a save location. */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_save_loc_t; @@ -121,7 +128,7 @@ typedef ucontext_t unw_tdep_context_t; typedef struct { /* no x86-64-specific auxiliary proc-info */ - char unused; + UNW_EMPTY_STRUCT } unw_tdep_proc_info_t; diff --git a/src/native/external/libunwind/include/libunwind.h.in b/src/native/external/libunwind/include/libunwind.h.in index 0ac692b71f74f6..5ed37ea575c138 100644 --- a/src/native/external/libunwind/include/libunwind.h.in +++ b/src/native/external/libunwind/include/libunwind.h.in @@ -23,8 +23,6 @@ # include "libunwind-x86.h" #elif defined __x86_64__ # include "libunwind-x86_64.h" -#elif defined __tilegx__ -# include "libunwind-tilegx.h" #elif defined __s390x__ # include "libunwind-s390x.h" #elif defined __riscv || defined __riscv__ diff --git a/src/native/external/libunwind/include/libunwind_i.h b/src/native/external/libunwind/include/libunwind_i.h index 3ca146258f6d28..1dbcb6a86d0f1b 100644 --- a/src/native/external/libunwind/include/libunwind_i.h +++ b/src/native/external/libunwind/include/libunwind_i.h @@ -51,6 +51,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include +#include #include #include #include @@ -58,6 +59,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include +#if defined(HAVE_SYS_SYSCALL_H) +# include /* For SYS_xxx definitions */ +#endif + #if defined(HAVE_ELF_H) # include #elif defined(HAVE_SYS_ELF_H) @@ -121,8 +126,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ static inline int byte_order_is_valid(int byte_order) { - return byte_order != UNW_BIG_ENDIAN - && byte_order != UNW_LITTLE_ENDIAN; + return byte_order == UNW_BIG_ENDIAN + || byte_order == UNW_LITTLE_ENDIAN; } static inline int @@ -146,7 +151,7 @@ target_is_big_endian(void) #ifdef DEBUG # define UNW_DEBUG 1 #else -# define UNW_DEBUG 0 +# undef UNW_DEBUG #endif /* Make it easy to write thread-safe code which may or may not be @@ -157,6 +162,7 @@ target_is_big_endian(void) #pragma weak pthread_mutex_init #pragma weak pthread_mutex_lock #pragma weak pthread_mutex_unlock +#pragma weak pthread_sigmask #define mutex_init(l) \ (pthread_mutex_init != NULL ? pthread_mutex_init ((l), NULL) : 0) @@ -184,8 +190,11 @@ static inline void mark_as_used(void *v UNUSED) { } #if defined(CONFIG_BLOCK_SIGNALS) +/* SIGPROCMASK ignores return values, so we do not have to correct for pthread_sigmask() returning + errno on failure when sigprocmask() returns -1. */ # define SIGPROCMASK(how, new_mask, old_mask) \ - sigprocmask((how), (new_mask), (old_mask)) + (pthread_sigmask != NULL ? pthread_sigmask((how), (new_mask), (old_mask)) \ + : sigprocmask((how), (new_mask), (old_mask))) #else # define SIGPROCMASK(how, new_mask, old_mask) mark_as_used(old_mask) #endif @@ -213,14 +222,46 @@ do { \ #define SOS_MEMORY_SIZE 16384 /* see src/mi/mempool.c */ +/* Provide an internal syscall version of mmap to improve signal safety. */ +static ALWAYS_INLINE void * +mi_mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ +#if defined(SYS_mmap) && !defined(__i386__) + /* Where supported, bypass libc and invoke the syscall directly. */ +# if defined(__FreeBSD__) // prefer over syscall on *BSD + long int ret = __syscall (SYS_mmap, addr, len, prot, flags, fd, offset); +# else + long int ret = syscall (SYS_mmap, addr, len, prot, flags, fd, offset); +# endif + // @todo this is very likely Linux specific + if ((unsigned long int)ret > -4096UL) + return MAP_FAILED; + else + return (void *)ret; +#else + /* Where direct syscalls are not supported, forward to the libc call. */ + return mmap (addr, len, prot, flags, fd, offset); +#endif +} + +/* Provide an internal syscall version of munmap to improve signal safety. */ +static ALWAYS_INLINE int +mi_munmap (void *addr, size_t len) +{ +#ifdef SYS_munmap + return syscall (SYS_munmap, addr, len); +#else + return munmap (addr, len); +#endif +} + #ifndef MAP_ANONYMOUS # define MAP_ANONYMOUS MAP_ANON #endif #define GET_MEMORY(mem, size) \ do { \ - /* Hopefully, mmap() goes straight through to a system call stub... */ \ - mem = mmap (NULL, size, PROT_READ | PROT_WRITE, \ - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \ + mem = mi_mmap (NULL, size, PROT_READ | PROT_WRITE, \ + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \ if (mem == MAP_FAILED) \ mem = NULL; \ } while (0) @@ -261,28 +302,53 @@ extern int unwi_dyn_validate_cache (unw_addr_space_t as, void *arg); extern unw_dyn_info_list_t _U_dyn_info_list; extern pthread_mutex_t _U_dyn_info_list_lock; -#if UNW_DEBUG -#define unwi_debug_level UNWI_ARCH_OBJ(debug_level) +#define unw_address_is_valid UNWI_ARCH_OBJ(address_is_valid) +HIDDEN bool unw_address_is_valid(unw_word_t, size_t); + + +#if defined(UNW_DEBUG) +# define unwi_debug_level UNWI_ARCH_OBJ(debug_level) extern long unwi_debug_level; +# include # include -# define Debug(level, /* format */ ...) \ -do { \ - if (unwi_debug_level >= level) \ - { \ - int _n = level; \ - if (_n > 16) \ - _n = 16; \ - fprintf (stderr, "%*c>%s: ", _n, ' ', __FUNCTION__); \ - fprintf (stderr, /* format */ __VA_ARGS__); \ - } \ -} while (0) +# include + +#define Debug(level, ...) _unw_debug(level, __FUNCTION__, __VA_ARGS__) + +/** + * Send a debug message to stderr. + * + * This function may be called from within a signal handler context where + * fprintf(3) is not safe to call. The write(2) call is safe, however, and we're + * going to have to assume that snprintf(3) is signal safe otherwise it's pretty + * pointless to use Debug() calls anywhere. + */ +static inline void _unw_debug(int level, char const * const fname, char const * const fmt, ...) +{ + if (unwi_debug_level >= level) + { + enum { buf_size = 512 }; + char buf[buf_size]; + + if (level > 16) level = 16; + int bcount = snprintf (buf, buf_size, "%*c>%s: ", level, ' ', fname); + int res = write(STDERR_FILENO, buf, bcount); + + va_list ap; + va_start(ap, fmt); + bcount = vsnprintf (buf, buf_size, fmt, ap); + va_end(ap); + res = write(STDERR_FILENO, buf, bcount); + (void)res; /* silence "variable set but not used" warning */ + } +} # define Dprintf(/* format */ ...) \ fprintf (stderr, /* format */ __VA_ARGS__) -#else +#else /* defined(UNW_DEBUG) */ # define Debug(level, /* format */ ...) # define Dprintf( /* format */ ...) -#endif +#endif /* defined(UNW_DEBUG) */ static ALWAYS_INLINE int print_error (const char *string) @@ -292,7 +358,7 @@ print_error (const char *string) HIDDEN extern long unw_page_size; -static inline unw_word_t uwn_page_start(unw_word_t addr) +static inline unw_word_t unw_page_start(unw_word_t addr) { return addr & ~(unw_page_size - 1); } @@ -326,7 +392,7 @@ struct elf_dyn_info static inline void invalidate_edi (struct elf_dyn_info *edi) { if (edi->ei.image) - munmap (edi->ei.image, edi->ei.size); + mi_munmap (edi->ei.image, edi->ei.size); memset (edi, 0, sizeof (*edi)); edi->di_cache.format = -1; edi->di_debug.format = -1; diff --git a/src/native/external/libunwind/include/remote.h b/src/native/external/libunwind/include/remote.h index 814e532cfa5ea4..99839f6622b862 100644 --- a/src/native/external/libunwind/include/remote.h +++ b/src/native/external/libunwind/include/remote.h @@ -8,8 +8,8 @@ #ifdef UNW_LOCAL_ONLY static inline int -fetch8 (unw_addr_space_t as, unw_accessors_t *a, - unw_word_t *addr, int8_t *valp, void *arg) +fetch8 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, + unw_word_t *addr, int8_t *valp, void *arg UNUSED) { *valp = *(int8_t *) (uintptr_t) *addr; *addr += 1; @@ -17,8 +17,8 @@ fetch8 (unw_addr_space_t as, unw_accessors_t *a, } static inline int -fetch16 (unw_addr_space_t as, unw_accessors_t *a, - unw_word_t *addr, int16_t *valp, void *arg) +fetch16 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, + unw_word_t *addr, int16_t *valp, void *arg UNUSED) { *valp = *(int16_t *) (uintptr_t) *addr; *addr += 2; @@ -26,8 +26,8 @@ fetch16 (unw_addr_space_t as, unw_accessors_t *a, } static inline int -fetch32 (unw_addr_space_t as, unw_accessors_t *a, - unw_word_t *addr, int32_t *valp, void *arg) +fetch32 (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, + unw_word_t *addr, int32_t *valp, void *arg UNUSED) { *valp = *(int32_t *) (uintptr_t) *addr; *addr += 4; @@ -35,8 +35,8 @@ fetch32 (unw_addr_space_t as, unw_accessors_t *a, } static inline int -fetchw (unw_addr_space_t as, unw_accessors_t *a, - unw_word_t *addr, unw_word_t *valp, void *arg) +fetchw (unw_addr_space_t as UNUSED, unw_accessors_t *a UNUSED, + unw_word_t *addr, unw_word_t *valp, void *arg UNUSED) { *valp = *(unw_word_t *) (uintptr_t) *addr; *addr += sizeof (unw_word_t); diff --git a/src/native/external/libunwind/include/remote/win/unistd.h b/src/native/external/libunwind/include/remote/win/unistd.h index d4ff227871b919..b20c38490c9ac5 100644 --- a/src/native/external/libunwind/include/remote/win/unistd.h +++ b/src/native/external/libunwind/include/remote/win/unistd.h @@ -28,4 +28,10 @@ long sysconf(int name); #define _SC_PAGESIZE 11 +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#define S_ISREG(x) 0 + #endif // _MSC_VER diff --git a/src/native/external/libunwind/include/tdep-aarch64/jmpbuf.h b/src/native/external/libunwind/include/tdep-aarch64/jmpbuf.h index 3f01a442baf909..8479bf2b587536 100644 --- a/src/native/external/libunwind/include/tdep-aarch64/jmpbuf.h +++ b/src/native/external/libunwind/include/tdep-aarch64/jmpbuf.h @@ -27,7 +27,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* FIXME for AArch64 */ +#if defined __FreeBSD__ +#define JB_SP 1 +#define JB_RP 13 +#define JB_MASK_SAVED 0 +#define JB_MASK 22 +#define _JB_STK_SHIFT 0 +#else #define JB_SP 13 #define JB_RP 14 #define JB_MASK_SAVED 15 #define JB_MASK 16 +#endif diff --git a/src/native/external/libunwind/include/tdep-aarch64/libunwind_i.h b/src/native/external/libunwind/include/tdep-aarch64/libunwind_i.h index d96833a219e4fa..ec1a2e91afd643 100644 --- a/src/native/external/libunwind/include/tdep-aarch64/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-aarch64/libunwind_i.h @@ -78,6 +78,9 @@ struct unw_addr_space { struct unw_accessors acc; int big_endian; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ @@ -96,16 +99,18 @@ struct cursor { AARCH64_SCF_NONE, AARCH64_SCF_LINUX_RT_SIGFRAME, + AARCH64_SCF_FREEBSD_RT_SIGFRAME, + AARCH64_SCF_QNX_RT_SIGFRAME, } sigcontext_format; unw_word_t sigcontext_addr; unw_word_t sigcontext_sp; unw_word_t sigcontext_pc; int validate; - ucontext_t *uc; + unw_context_t *uc; }; -static inline ucontext_t * +static inline unw_context_t * dwarf_get_uc(const struct dwarf_cursor *cursor) { const struct cursor *c = (struct cursor *) cursor->as_arg; @@ -273,7 +278,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_getcontext_trace UNW_ARCH_OBJ(getcontext_trace) #define tdep_init_done UNW_OBJ(init_done) -#define tdep_init_mem_validate UNW_OBJ(init_mem_validate) #define tdep_init UNW_OBJ(init) /* Platforms that support UNW_INFO_FORMAT_TABLE need to define tdep_search_unwind_table. */ @@ -312,11 +316,10 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) extern atomic_bool tdep_init_done; extern void tdep_init (void); -extern void tdep_init_mem_validate (void); extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg); -extern void *tdep_uc_addr (unw_tdep_context_t *uc, int reg); +extern void *tdep_uc_addr (unw_context_t *uc, int reg); extern int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, unsigned long *segbase, unsigned long *mapoff, char *path, size_t pathlen); @@ -328,6 +331,6 @@ extern int tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, extern int tdep_trace (unw_cursor_t *cursor, void **addresses, int *n); extern void tdep_stash_frame (struct dwarf_cursor *c, struct dwarf_reg_state *rs); -extern int tdep_getcontext_trace (unw_tdep_context_t *); +extern int tdep_getcontext_trace (unw_context_t *); #endif /* AARCH64_LIBUNWIND_I_H */ diff --git a/src/native/external/libunwind/include/tdep-arm/libunwind_i.h b/src/native/external/libunwind/include/tdep-arm/libunwind_i.h index 5bd28c953a6199..35b13c79fbaca0 100644 --- a/src/native/external/libunwind/include/tdep-arm/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-arm/libunwind_i.h @@ -64,6 +64,9 @@ struct unw_addr_space { struct unw_accessors acc; int big_endian; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-hppa/libunwind_i.h b/src/native/external/libunwind/include/tdep-hppa/libunwind_i.h index fd5910d0125a34..1b6757fb13610c 100644 --- a/src/native/external/libunwind/include/tdep-hppa/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-hppa/libunwind_i.h @@ -46,6 +46,9 @@ unw_tdep_frame_t; struct unw_addr_space { struct unw_accessors acc; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-ia64/libunwind_i.h b/src/native/external/libunwind/include/tdep-ia64/libunwind_i.h index 5b0defc8ef1096..04ca58f5170a2c 100644 --- a/src/native/external/libunwind/include/tdep-ia64/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-ia64/libunwind_i.h @@ -97,6 +97,9 @@ struct unw_addr_space struct unw_accessors acc; int big_endian; int abi; /* abi < 0 => unknown, 0 => SysV, 1 => HP-UX, 2 => Windows */ +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; diff --git a/src/native/external/libunwind/include/tdep-loongarch64/libunwind_i.h b/src/native/external/libunwind/include/tdep-loongarch64/libunwind_i.h index a16870f4737d55..d21c9229766e34 100644 --- a/src/native/external/libunwind/include/tdep-loongarch64/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-loongarch64/libunwind_i.h @@ -45,6 +45,9 @@ struct unw_addr_space { struct unw_accessors acc; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ @@ -230,10 +233,8 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_get_ip(c) ((c)->dwarf.ip) extern atomic_bool tdep_init_done; -#define tdep_init_mem_validate UNW_OBJ(init_mem_validate) extern void tdep_init (void); -extern void tdep_init_mem_validate (void); extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg); diff --git a/src/native/external/libunwind/include/tdep-mips/libunwind_i.h b/src/native/external/libunwind/include/tdep-mips/libunwind_i.h index 343a195141be8d..b0e623499d053d 100644 --- a/src/native/external/libunwind/include/tdep-mips/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-mips/libunwind_i.h @@ -54,6 +54,9 @@ struct unw_addr_space mips_abi_t abi; unsigned int addr_size; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-ppc32/libunwind_i.h b/src/native/external/libunwind/include/tdep-ppc32/libunwind_i.h index b9100e6ac894c7..46d4f5a8ed9d68 100644 --- a/src/native/external/libunwind/include/tdep-ppc32/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-ppc32/libunwind_i.h @@ -52,6 +52,9 @@ unw_tdep_frame_t; struct unw_addr_space { struct unw_accessors acc; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-ppc64/libunwind_i.h b/src/native/external/libunwind/include/tdep-ppc64/libunwind_i.h index 48227c76e20916..a93d5693184334 100644 --- a/src/native/external/libunwind/include/tdep-ppc64/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-ppc64/libunwind_i.h @@ -54,6 +54,9 @@ struct unw_addr_space struct unw_accessors acc; int big_endian; ppc64_abi_t abi; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-riscv/libunwind_i.h b/src/native/external/libunwind/include/tdep-riscv/libunwind_i.h index 4404a47510a4ce..951de12a0bc942 100644 --- a/src/native/external/libunwind/include/tdep-riscv/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-riscv/libunwind_i.h @@ -60,6 +60,9 @@ struct unw_addr_space int big_endian; unsigned int addr_size; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ @@ -161,7 +164,6 @@ dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) { char *valp = (char *) &val; unw_word_t addr; - int ret; if (DWARF_IS_NULL_LOC (loc)) return -UNW_EBADREG; @@ -185,7 +187,6 @@ dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) { char *valp = (char *) &val; unw_word_t addr; - int ret; if (DWARF_IS_NULL_LOC (loc)) return -UNW_EBADREG; @@ -247,7 +248,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #endif /* !UNW_LOCAL_ONLY */ #define tdep_getcontext_trace unw_getcontext -#define tdep_init_mem_validate UNW_OBJ(init_mem_validate) #define tdep_init_done UNW_OBJ(init_done) #define tdep_init UNW_OBJ(init) /* Platforms that support UNW_INFO_FORMAT_TABLE need to define @@ -286,7 +286,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) extern atomic_bool tdep_init_done; extern void tdep_init (void); -extern void tdep_init_mem_validate (void); extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg); diff --git a/src/native/external/libunwind/include/tdep-s390x/libunwind_i.h b/src/native/external/libunwind/include/tdep-s390x/libunwind_i.h index ba75c0742f62cd..a6af60c9c61d5f 100644 --- a/src/native/external/libunwind/include/tdep-s390x/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-s390x/libunwind_i.h @@ -42,6 +42,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ struct unw_addr_space { struct unw_accessors acc; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ @@ -203,7 +206,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_getcontext_trace unw_getcontext #define tdep_init_done UNW_OBJ(init_done) -#define tdep_init_mem_validate UNW_OBJ(init_mem_validate) #define tdep_init UNW_OBJ(init) /* Platforms that support UNW_INFO_FORMAT_TABLE need to define tdep_search_unwind_table. */ @@ -242,7 +244,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) extern atomic_bool tdep_init_done; extern void tdep_init (void); -extern void tdep_init_mem_validate (void); extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg); diff --git a/src/native/external/libunwind/include/tdep-sh/libunwind_i.h b/src/native/external/libunwind/include/tdep-sh/libunwind_i.h index 7b4fe0023b4d52..4f4a5cdd067517 100644 --- a/src/native/external/libunwind/include/tdep-sh/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-sh/libunwind_i.h @@ -47,6 +47,9 @@ struct unw_addr_space { struct unw_accessors acc; int big_endian; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-tilegx/dwarf-config.h b/src/native/external/libunwind/include/tdep-tilegx/dwarf-config.h deleted file mode 100644 index 93bc6aaecced65..00000000000000 --- a/src/native/external/libunwind/include/tdep-tilegx/dwarf-config.h +++ /dev/null @@ -1,50 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef dwarf_config_h -#define dwarf_config_h - -/* This is FIRST_PSEUDO_REGISTER in GCC, since DWARF_FRAME_REGISTERS is not - explicitly defined. */ -#define DWARF_NUM_PRESERVED_REGS 188 - -#define DWARF_REGNUM_MAP_LENGTH (56 + 2) - -/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */ -#define dwarf_is_big_endian(addr_space) ((addr_space)->big_endian) - -/* Convert a pointer to a dwarf_cursor structure to a pointer to - unw_cursor_t. */ -#define dwarf_to_cursor(c) ((unw_cursor_t *) (c)) - -typedef struct dwarf_loc -{ - unw_word_t val; -#ifndef UNW_LOCAL_ONLY - unw_word_t type; /* see DWARF_LOC_TYPE_* macros. */ -#endif -} dwarf_loc_t; - -#endif /* dwarf_config_h */ diff --git a/src/native/external/libunwind/include/tdep-tilegx/jmpbuf.h b/src/native/external/libunwind/include/tdep-tilegx/jmpbuf.h deleted file mode 100644 index 3afe9e46cc499a..00000000000000 --- a/src/native/external/libunwind/include/tdep-tilegx/jmpbuf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -/* Use glibc's jump-buffer indices; NPTL peeks at SP: */ - -/* FIXME for Tilegx! */ - -#define JB_SP 4 -#define JB_RP 5 -#define JB_MASK_SAVED 6 -#define JB_MASK 7 diff --git a/src/native/external/libunwind/include/tdep-tilegx/libunwind_i.h b/src/native/external/libunwind/include/tdep-tilegx/libunwind_i.h deleted file mode 100644 index dc4cb7fdb2e0e1..00000000000000 --- a/src/native/external/libunwind/include/tdep-tilegx/libunwind_i.h +++ /dev/null @@ -1,260 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef TILEGX_LIBUNWIND_I_H -#define TILEGX_LIBUNWIND_I_H - -/* Target-dependent definitions that are internal to libunwind but need - to be shared with target-independent code. */ - -#include -#include -#include - -# include "elf64.h" -#include "mempool.h" -#include "dwarf.h" - -typedef struct -{ - /* no Tilegx-specific fast trace */ -} unw_tdep_frame_t; - -struct unw_addr_space -{ - struct unw_accessors acc; - - int big_endian; - tilegx_abi_t abi; - unsigned int addr_size; - - unw_caching_policy_t caching_policy; - _Atomic uint32_t cache_generation; - unw_word_t dyn_generation; /* see dyn-common.h */ - unw_word_t dyn_info_list_addr; /* (cached) dyn_info_list_addr */ - struct dwarf_rs_cache global_cache; - struct unw_debug_frame_list *debug_frames; -}; - -#define tdep_big_endian(as) ((as)->big_endian) - -struct cursor -{ - struct dwarf_cursor dwarf; /* must be first */ - unw_word_t sigcontext_addr; - unw_word_t sigcontext_sp; - unw_word_t sigcontext_pc; -}; - -#define DWARF_GET_LOC(l) ((l).val) - -#ifndef UNW_REMOTE_ONLY -typedef long tilegx_reg_t; -#endif - -#ifdef UNW_LOCAL_ONLY -#define DWARF_NULL_LOC DWARF_LOC (0, 0) -#define DWARF_IS_NULL_LOC(l) (DWARF_GET_LOC (l) == 0) -#define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r) }) -#define DWARF_IS_REG_LOC(l) 0 -#define DWARF_REG_LOC(c,r) (DWARF_LOC((unw_word_t) (intptr_t) \ - tdep_uc_addr((c)->as_arg, (r)), 0)) -#define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) -#define DWARF_FPREG_LOC(c,r) (DWARF_LOC((unw_word_t) (intptr_t) \ - tdep_uc_addr((c)->as_arg, (r)), 0)) - -/* Tilegx has no FP. */ -static inline int -dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) -{ - Debug (1, "Tielgx has no fp!\n"); - abort(); - return 0; -} - -static inline int -dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) -{ - Debug (1, "Tielgx has no fp!\n"); - abort(); - return 0; -} - -static inline int -dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) -{ - if (!DWARF_GET_LOC (loc)) - return -1; - - *val = *(tilegx_reg_t *) (intptr_t) DWARF_GET_LOC (loc); - return 0; -} - -static inline int -dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) -{ - if (!DWARF_GET_LOC (loc)) - return -1; - - *(tilegx_reg_t *) (intptr_t) DWARF_GET_LOC (loc) = val; - return 0; -} - -#else /* !UNW_LOCAL_ONLY */ -#define DWARF_LOC_TYPE_FP (1 << 0) -#define DWARF_LOC_TYPE_REG (1 << 1) -#define DWARF_NULL_LOC DWARF_LOC (0, 0) -#define DWARF_IS_NULL_LOC(l) \ - ({ dwarf_loc_t _l = (l); _l.val == 0 && _l.type == 0; }) -#define DWARF_LOC(r, t) ((dwarf_loc_t) { .val = (r), .type = (t) }) -#define DWARF_IS_REG_LOC(l) (((l).type & DWARF_LOC_TYPE_REG) != 0) -#define DWARF_IS_FP_LOC(l) (((l).type & DWARF_LOC_TYPE_FP) != 0) -#define DWARF_REG_LOC(c,r) DWARF_LOC((r), DWARF_LOC_TYPE_REG) -#define DWARF_MEM_LOC(c,m) DWARF_LOC ((m), 0) -#define DWARF_FPREG_LOC(c,r) DWARF_LOC((r), (DWARF_LOC_TYPE_REG \ - | DWARF_LOC_TYPE_FP)) - -/* TILEGX has no fp. */ -static inline int -dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) -{ - Debug (1, "Tielgx has no fp!\n"); - abort(); - return 0; -} - -static inline int -dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) -{ - Debug (1, "Tielgx has no fp!\n"); - abort(); - return 0; -} - -static inline int -dwarf_get (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t *val) -{ - if (DWARF_IS_NULL_LOC (loc)) - return -UNW_EBADREG; - - /* If a code-generator were to save a value of type unw_word_t in a - floating-point register, we would have to support this case. I - suppose it could happen with MMX registers, but does it really - happen? */ - assert (!DWARF_IS_FP_LOC (loc)); - - if (DWARF_IS_REG_LOC (loc)) - return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), val, - 0, c->as_arg); - - return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), val, - 0, c->as_arg); -} - -static inline int -dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) -{ - if (DWARF_IS_NULL_LOC (loc)) - return -UNW_EBADREG; - - /* If a code-generator were to save a value of type unw_word_t in a - floating-point register, we would have to support this case. I - suppose it could happen with MMX registers, but does it really - happen? */ - assert (!DWARF_IS_FP_LOC (loc)); - - if (DWARF_IS_REG_LOC (loc)) - return (*c->as->acc.access_reg) (c->as, DWARF_GET_LOC (loc), &val, - 1, c->as_arg); - - return (*c->as->acc.access_mem) (c->as, DWARF_GET_LOC (loc), &val, - 1, c->as_arg); -} - -#endif /* !UNW_LOCAL_ONLY */ - -#define tdep_getcontext_trace unw_getcontext -#define tdep_init_done UNW_OBJ(init_done) -#define tdep_needs_initialization UNW_OBJ(needs_initialization) -#define tdep_init UNW_OBJ(init) -/* Platforms that support UNW_INFO_FORMAT_TABLE need to define - tdep_search_unwind_table. */ -#define tdep_search_unwind_table dwarf_search_unwind_table -#define tdep_find_unwind_table dwarf_find_unwind_table -#define tdep_uc_addr UNW_ARCH_OBJ(uc_addr) -#define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image) -#define tdep_get_exe_image_path UNW_ARCH_OBJ(get_exe_image_path) -#define tdep_access_reg UNW_OBJ(access_reg) -#define tdep_access_fpreg UNW_OBJ(access_fpreg) -#define tdep_fetch_frame(c,ip,n) do {} while(0) -#define tdep_cache_frame(c) 0 -#define tdep_reuse_frame(c,frame) do {} while(0) -#define tdep_stash_frame(c,rs) do {} while(0) -#define tdep_trace(cur,addr,n) (-UNW_ENOINFO) - -#ifdef UNW_LOCAL_ONLY -#define tdep_find_proc_info(c,ip,n) \ - dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n), \ - (c)->as_arg) -#define tdep_put_unwind_info(as,pi,arg) \ - dwarf_put_unwind_info((as), (pi), (arg)) -#else -#define tdep_find_proc_info(c,ip,n) \ - (*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \ - (c)->as_arg) -#define tdep_put_unwind_info(as,pi,arg) \ - (*(as)->acc.put_unwind_info)((as), (pi), (arg)) -#endif - -#define tdep_get_as(c) ((c)->dwarf.as) -#define tdep_get_as_arg(c) ((c)->dwarf.as_arg) -#define tdep_get_ip(c) ((c)->dwarf.ip) - -extern atomic_bool tdep_init_done; - -extern void tdep_init (void); -extern int tdep_search_unwind_table (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, - void *arg); -extern void *tdep_uc_addr (ucontext_t *uc, int reg); -extern int tdep_get_elf_image (struct elf_image *ei, - pid_t pid, unw_word_t ip, - unsigned long *segbase, - unsigned long *mapoff, - char *path, size_t pathlen); -extern void tdep_get_exe_image_path (char *path); -extern int tdep_access_reg (struct cursor *c, - unw_regnum_t reg, - unw_word_t *valp, - int write); -extern int tdep_access_fpreg (struct cursor *c, - unw_regnum_t reg, - unw_fpreg_t *valp, - int write); - -#endif /* TILEGX_LIBUNWIND_I_H */ diff --git a/src/native/external/libunwind/include/tdep-x86/jmpbuf.h b/src/native/external/libunwind/include/tdep-x86/jmpbuf.h index 521dfa6992b36e..625dac32693445 100644 --- a/src/native/external/libunwind/include/tdep-x86/jmpbuf.h +++ b/src/native/external/libunwind/include/tdep-x86/jmpbuf.h @@ -38,5 +38,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define JB_RP 0 #define JB_MASK_SAVED 11 #define JB_MASK 7 +#define _JB_STK_SHIFT 4 #endif diff --git a/src/native/external/libunwind/include/tdep-x86/libunwind_i.h b/src/native/external/libunwind/include/tdep-x86/libunwind_i.h index ad4edc2f5ac849..58e583c3b3f27a 100644 --- a/src/native/external/libunwind/include/tdep-x86/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-x86/libunwind_i.h @@ -46,6 +46,9 @@ unw_tdep_frame_t; struct unw_addr_space { struct unw_accessors acc; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ diff --git a/src/native/external/libunwind/include/tdep-x86_64/jmpbuf.h b/src/native/external/libunwind/include/tdep-x86_64/jmpbuf.h index b2e5332b059661..c5dd87a777af96 100644 --- a/src/native/external/libunwind/include/tdep-x86_64/jmpbuf.h +++ b/src/native/external/libunwind/include/tdep-x86_64/jmpbuf.h @@ -37,8 +37,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define JB_SP 2 #define JB_RP 0 +#ifdef __amd64__ +#define JB_MASK_SAVED 11 +#else /* Pretend the ip cannot be 0 and mask is always saved */ #define JB_MASK_SAVED 0 +#endif #define JB_MASK 9 +#define _JB_STK_SHIFT 8 #endif diff --git a/src/native/external/libunwind/include/tdep-x86_64/libunwind_i.h b/src/native/external/libunwind/include/tdep-x86_64/libunwind_i.h index ff0436a09c42a2..7ec16aafdcdc02 100644 --- a/src/native/external/libunwind/include/tdep-x86_64/libunwind_i.h +++ b/src/native/external/libunwind/include/tdep-x86_64/libunwind_i.h @@ -65,6 +65,9 @@ unw_tdep_frame_t; struct unw_addr_space { struct unw_accessors acc; +#ifndef UNW_REMOTE_ONLY + unw_iterate_phdr_func_t iterate_phdr_function; +#endif unw_caching_policy_t caching_policy; _Atomic uint32_t cache_generation; unw_word_t dyn_generation; /* see dyn-common.h */ @@ -172,7 +175,7 @@ dwarf_is_null_loc(dwarf_loc_t l) #endif /* !UNW_LOCAL_ONLY */ static inline int -dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) +dwarf_getfp (struct dwarf_cursor *c UNUSED, dwarf_loc_t loc, unw_fpreg_t *val UNUSED) { if (DWARF_IS_NULL_LOC (loc)) return -UNW_EBADREG; @@ -181,7 +184,7 @@ dwarf_getfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t *val) } static inline int -dwarf_putfp (struct dwarf_cursor *c, dwarf_loc_t loc, unw_fpreg_t val) +dwarf_putfp (struct dwarf_cursor *c UNUSED, dwarf_loc_t loc, unw_fpreg_t val UNUSED) { if (DWARF_IS_NULL_LOC (loc)) return -UNW_EBADREG; @@ -224,7 +227,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) #define tdep_getcontext_trace UNW_ARCH_OBJ(getcontext_trace) #define tdep_init_done UNW_OBJ(init_done) -#define tdep_init_mem_validate UNW_OBJ(init_mem_validate) #define tdep_init UNW_OBJ(init) /* Platforms that support UNW_INFO_FORMAT_TABLE need to define tdep_search_unwind_table. */ @@ -269,7 +271,6 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) extern atomic_bool tdep_init_done; extern void tdep_init (void); -extern void tdep_init_mem_validate (void); extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, unw_dyn_info_t *di, unw_proc_info_t *pi, int need_unwind_info, void *arg); @@ -288,9 +289,9 @@ extern void tdep_fetch_frame (struct dwarf_cursor *c, unw_word_t ip, extern int tdep_cache_frame (struct dwarf_cursor *c); extern void tdep_reuse_frame (struct dwarf_cursor *c, int frame); +#endif extern void tdep_stash_frame (struct dwarf_cursor *c, struct dwarf_reg_state *rs); -#endif extern int tdep_getcontext_trace (unw_tdep_context_t *); extern int tdep_trace (unw_cursor_t *cursor, void **addresses, int *n); diff --git a/src/native/external/libunwind/include/tdep/dwarf-config.h b/src/native/external/libunwind/include/tdep/dwarf-config.h index a37f4220e3574d..67ebcc934bd429 100644 --- a/src/native/external/libunwind/include/tdep/dwarf-config.h +++ b/src/native/external/libunwind/include/tdep/dwarf-config.h @@ -23,8 +23,6 @@ # include "tdep-x86/dwarf-config.h" #elif defined __x86_64__ || defined __amd64__ # include "tdep-x86_64/dwarf-config.h" -#elif defined __tilegx__ -# include "tdep-tilegx/dwarf-config.h" #elif defined __riscv || defined __riscv__ # include "tdep-riscv/dwarf-config.h" #elif defined __loongarch64 diff --git a/src/native/external/libunwind/include/tdep/jmpbuf.h b/src/native/external/libunwind/include/tdep/jmpbuf.h index 01b9ee0245e82c..0c033336ae949a 100644 --- a/src/native/external/libunwind/include/tdep/jmpbuf.h +++ b/src/native/external/libunwind/include/tdep/jmpbuf.h @@ -21,8 +21,6 @@ # include "tdep-x86/jmpbuf.h" #elif defined __x86_64__ # include "tdep-x86_64/jmpbuf.h" -#elif defined __tilegx__ -# include "tdep-tilegx/jmpbuf.h" #elif defined __riscv || defined __riscv__ # include "tdep-riscv/jmpbuf.h" #elif defined __loongarch64 diff --git a/src/native/external/libunwind/include/tdep/libunwind_i.h.in b/src/native/external/libunwind/include/tdep/libunwind_i.h.in index 1bacc3a8235ead..f026744665dceb 100644 --- a/src/native/external/libunwind/include/tdep/libunwind_i.h.in +++ b/src/native/external/libunwind/include/tdep/libunwind_i.h.in @@ -23,8 +23,6 @@ # include "tdep-x86/libunwind_i.h" #elif defined __x86_64__ # include "tdep-x86_64/libunwind_i.h" -#elif defined __tilegx__ -# include "tdep-tilegx/libunwind_i.h" #elif defined __s390x__ # include "tdep-s390x/libunwind_i.h" #elif defined __riscv || defined __riscv__ diff --git a/src/native/external/libunwind/src/CMakeLists.txt b/src/native/external/libunwind/src/CMakeLists.txt index eee31afa6aae4a..0284d584bb7d76 100644 --- a/src/native/external/libunwind/src/CMakeLists.txt +++ b/src/native/external/libunwind/src/CMakeLists.txt @@ -21,6 +21,7 @@ elseif(TARGET_AARCH64) endif() SET(libunwind_ptrace_la_SOURCES + mi/init.c ptrace/_UPT_elf.c ptrace/_UPT_accessors.c ptrace/_UPT_access_fpreg.c ptrace/_UPT_access_mem.c ptrace/_UPT_access_reg.c @@ -28,6 +29,7 @@ SET(libunwind_ptrace_la_SOURCES ptrace/_UPT_find_proc_info.c ptrace/_UPT_get_dyn_info_list_addr.c ptrace/_UPT_put_unwind_info.c ptrace/_UPT_get_proc_name.c ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c + ptrace/_UPT_get_elf_filename.c ) SET(libunwind_coredump_la_SOURCES @@ -38,7 +40,9 @@ SET(libunwind_coredump_la_SOURCES coredump/_UCD_elf_map_image.c coredump/_UCD_find_proc_info.c coredump/_UCD_get_proc_name.c + coredump/_UCD_get_elf_filename.c + mi/init.c coredump/_UPT_elf.c coredump/_UPT_access_fpreg.c coredump/_UPT_get_dyn_info_list_addr.c @@ -58,6 +62,8 @@ SET(libunwind_la_SOURCES_generic mi/Gget_fpreg.c mi/Gset_fpreg.c mi/Gset_caching_policy.c mi/Gset_cache_size.c + mi/Gset_iterate_phdr_function.c + mi/Gget_elf_filename.c ) SET(libunwind_la_SOURCES_os_linux @@ -141,6 +147,8 @@ SET(libunwind_la_SOURCES_local_nounwind mi/Lget_fpreg.c mi/Lset_fpreg.c mi/Lset_caching_policy.c mi/Lset_cache_size.c + mi/Lset_iterate_phdr_function.c + mi/Lget_elf_filename.c ) SET(libunwind_la_SOURCES_local diff --git a/src/native/external/libunwind/src/Makefile.am b/src/native/external/libunwind/src/Makefile.am index 9bfb5ded1a52f0..63041fd306f010 100644 --- a/src/native/external/libunwind/src/Makefile.am +++ b/src/native/external/libunwind/src/Makefile.am @@ -1,179 +1,328 @@ -SOVERSION=8:1:0 # See comments at end of file. +# +# This file is a part of the libunwind project. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# This permission notice shall be included in all copies or substantial portions +# of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +# Set the DSO versions +SOVERSION=9:0:1 # See comments at end of file. SETJMP_SO_VERSION=0:0:0 COREDUMP_SO_VERSION=0:0:0 + # # Don't link with start-files since we don't use any constructors/destructors: # COMMON_SO_LDFLAGS = $(LDFLAGS_NOSTARTFILES) -lib_LIBRARIES = +# +# Which libraries to build and install +# +# Order is important here. The names of the libraries need to end up in reverse +# dependency order for `make install` to do its job properly. +# lib_LTLIBRARIES = if !REMOTE_ONLY -lib_LTLIBRARIES += libunwind.la -if BUILD_PTRACE -lib_LTLIBRARIES += libunwind-ptrace.la + lib_LTLIBRARIES += libunwind.la +endif +if ARCH_AARCH64 + lib_LTLIBRARIES += libunwind-aarch64.la +endif +if ARCH_ARM + lib_LTLIBRARIES += libunwind-arm.la +endif +if ARCH_HPPA + lib_LTLIBRARIES += libunwind-hppa.la +endif +if ARCH_IA64 + lib_LTLIBRARIES += libunwind-ia64.la +endif +if ARCH_LOONGARCH64 + lib_LTLIBRARIES += libunwind-loongarch64.la +endif +if ARCH_MIPS + lib_LTLIBRARIES += libunwind-mips.la +endif +if ARCH_PPC32 + lib_LTLIBRARIES += libunwind-ppc32.la +endif +if ARCH_PPC64 + lib_LTLIBRARIES += libunwind-ppc64.la +endif +if ARCH_RISCV + lib_LTLIBRARIES += libunwind-riscv.la +endif +if ARCH_S390X + lib_LTLIBRARIES += libunwind-s390x.la endif +if ARCH_SH + lib_LTLIBRARIES += libunwind-sh.la +endif +if ARCH_X86 + lib_LTLIBRARIES += libunwind-x86.la +endif +if ARCH_X86_64 + lib_LTLIBRARIES += libunwind-x86_64.la +endif + if BUILD_COREDUMP -lib_LTLIBRARIES += libunwind-coredump.la + lib_LTLIBRARIES += libunwind-coredump.la endif +if BUILD_NTO + lib_LTLIBRARIES += libunwind-nto.la +endif +if BUILD_PTRACE + lib_LTLIBRARIES += libunwind-ptrace.la +endif +if BUILD_SETJMP + lib_LTLIBRARIES += libunwind-setjmp.la endif noinst_HEADERS = noinst_LTLIBRARIES = +if USE_ELF32 +libunwind_elf_libs = libunwind-elf32.la +endif +if USE_ELF64 +libunwind_elf_libs = libunwind-elf64.la +endif +if USE_ELFXX +libunwind_elf_libs = libunwind-elfxx.la +endif + +# If local unwinding is being built, link in the local unwinding functions +libunwind_libadd = +if !REMOTE_ONLY + libunwind_libadd += libunwind.la -lc +endif + +### pkg-config: pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libunwind-generic.pc if !REMOTE_ONLY pkgconfig_DATA += unwind/libunwind.pc endif - +if BUILD_COREDUMP +pkgconfig_DATA += coredump/libunwind-coredump.pc +endif if BUILD_PTRACE pkgconfig_DATA += ptrace/libunwind-ptrace.pc endif - if BUILD_SETJMP pkgconfig_DATA += setjmp/libunwind-setjmp.pc endif -if BUILD_COREDUMP -pkgconfig_DATA += coredump/libunwind-coredump.pc -endif - -### libunwind-ptrace: -libunwind_ptrace_la_SOURCES = \ - ptrace/_UPT_elf.c \ - ptrace/_UPT_accessors.c ptrace/_UPT_access_fpreg.c \ - ptrace/_UPT_access_mem.c ptrace/_UPT_access_reg.c \ - ptrace/_UPT_create.c ptrace/_UPT_destroy.c \ - ptrace/_UPT_find_proc_info.c ptrace/_UPT_get_dyn_info_list_addr.c \ - ptrace/_UPT_put_unwind_info.c ptrace/_UPT_get_proc_name.c \ - ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c -noinst_HEADERS += ptrace/_UPT_internal.h - ### libunwind-coredump: -libunwind_coredump_la_SOURCES = \ - coredump/_UCD_accessors.c \ - coredump/_UCD_create.c \ - coredump/_UCD_destroy.c \ - coredump/_UCD_access_mem.c \ - coredump/_UCD_elf_map_image.c \ - coredump/_UCD_find_proc_info.c \ - coredump/_UCD_get_proc_name.c \ - coredump/_UCD_corefile_elf.c \ - coredump/ucd_file_table.c \ +noinst_HEADERS += coredump/_UCD_internal.h \ + coredump/_UCD_lib.h \ + coredump/ucd_file_table.h +libunwind_coredump_la_SOURCES = \ + coredump/_UCD_access_mem.c \ + coredump/_UCD_accessors.c \ + coredump/_UCD_corefile_elf.c \ + coredump/_UCD_create.c \ + coredump/_UCD_destroy.c \ + coredump/_UCD_elf_map_image.c \ + coredump/ucd_file_table.c \ + coredump/_UCD_find_proc_info.c \ + coredump/_UCD_get_proc_name.c \ + coredump/_UCD_get_elf_filename.c \ \ - coredump/_UPT_elf.c \ - coredump/_UPT_access_fpreg.c \ + mi/init.c \ + coredump/_UPT_elf.c \ + coredump/_UPT_access_fpreg.c \ coredump/_UPT_get_dyn_info_list_addr.c \ - coredump/_UPT_put_unwind_info.c \ + coredump/_UPT_put_unwind_info.c \ coredump/_UPT_resume.c -libunwind_coredump_la_LDFLAGS = $(COMMON_SO_LDFLAGS) \ - -version-info $(COREDUMP_SO_VERSION) -libunwind_coredump_la_LIBADD = $(LIBLZMA) $(LIBZ) -noinst_HEADERS += coredump/_UCD_internal.h \ - coredump/_UCD_lib.h \ - coredump/ucd_file_table.h +libunwind_coredump_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(COREDUMP_SO_VERSION) +libunwind_coredump_la_LIBADD = \ + libunwind-$(arch).la \ + $(LIBLZMA) $(LIBZ) + +### libunwind-nto: +noinst_HEADERS += nto/unw_nto_internal.h +libunwind_nto_la_SOURCES = \ + mi/init.c \ + nto/unw_nto_access_fpreg.c \ + nto/unw_nto_access_mem.c \ + nto/unw_nto_accessors.c \ + nto/unw_nto_access_reg.c \ + nto/unw_nto_create.c \ + nto/unw_nto_destroy.c \ + nto/unw_nto_elf.c \ + nto/unw_nto_find_proc_info.c \ + nto/unw_nto_get_dyn_info_list_addr.c \ + nto/unw_nto_get_proc_name.c \ + nto/unw_nto_get_elf_filename.c \ + nto/unw_nto_put_unwind_info.c \ + nto/unw_nto_resume.c +libunwind_nto_la_LIBADD = \ + libunwind-$(arch).la \ + $(libunwind_libadd) -### libunwind-setjmp: -libunwind_setjmp_la_LDFLAGS = $(COMMON_SO_LDFLAGS) \ - -version-info $(SETJMP_SO_VERSION) - -if USE_ELF32 -LIBUNWIND_ELF = libunwind-elf32.la -endif -if USE_ELF64 -LIBUNWIND_ELF = libunwind-elf64.la -endif -if USE_ELFXX -LIBUNWIND_ELF = libunwind-elfxx.la -endif +### libunwind-ptrace: +noinst_HEADERS += ptrace/_UPT_internal.h +libunwind_ptrace_la_SOURCES = \ + mi/init.c \ + ptrace/_UPT_access_fpreg.c \ + ptrace/_UPT_access_mem.c \ + ptrace/_UPT_accessors.c \ + ptrace/_UPT_access_reg.c \ + ptrace/_UPT_create.c \ + ptrace/_UPT_destroy.c \ + ptrace/_UPT_elf.c \ + ptrace/_UPT_find_proc_info.c \ + ptrace/_UPT_get_dyn_info_list_addr.c \ + ptrace/_UPT_get_proc_name.c \ + ptrace/_UPT_get_elf_filename.c \ + ptrace/_UPT_put_unwind_info.c \ + ptrace/_UPT_reg_offset.c \ + ptrace/_UPT_resume.c +libunwind_ptrace_la_LIBADD = \ + libunwind-$(arch).la \ + $(LIBLZMA) $(LIBZ) -libunwind_setjmp_la_LIBADD = $(LIBUNWIND_ELF) \ - libunwind-$(arch).la \ - libunwind.la -lc -libunwind_setjmp_la_SOURCES = setjmp/longjmp.c \ - setjmp/siglongjmp.c -noinst_HEADERS += setjmp/setjmp_i.h +### libunwind-setjmp: +noinst_HEADERS += setjmp/setjmp_i.h +libunwind_setjmp_la_SOURCES = \ + mi/init.c \ + setjmp/longjmp.c \ + setjmp/siglongjmp.c +libunwind_setjmp_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SETJMP_SO_VERSION) +libunwind_setjmp_la_LIBADD = \ + $(libunwind_elf_libs) \ + libunwind-$(arch).la \ + $(libunwind_libadd) ### libunwind: libunwind_la_LIBADD = # List of arch-independent files needed by both local-only and generic # libraries: -libunwind_la_SOURCES_common = \ - $(libunwind_la_SOURCES_os) \ - mi/init.c mi/flush_cache.c mi/mempool.c mi/strerror.c +libunwind_la_SOURCES_common = \ + $(libunwind_la_SOURCES_os) \ + mi/init.c \ + mi/flush_cache.c \ + mi/mempool.c \ + mi/strerror.c # List of arch-independent files needed by generic library (libunwind-$ARCH): -libunwind_la_SOURCES_generic = \ - mi/Gdyn-extract.c mi/Gdyn-remote.c mi/Gfind_dynamic_proc_info.c \ - mi/Gget_accessors.c \ - mi/Gget_proc_info_by_ip.c mi/Gget_proc_name.c \ - mi/Gput_dynamic_unwind_info.c mi/Gdestroy_addr_space.c \ - mi/Gget_reg.c mi/Gset_reg.c \ - mi/Gget_fpreg.c mi/Gset_fpreg.c \ - mi/Gset_caching_policy.c \ - mi/Gset_cache_size.c +libunwind_la_SOURCES_generic = \ + mi/Gaddress_validator.c \ + mi/Gdestroy_addr_space.c \ + mi/Gdyn-extract.c \ + mi/Gdyn-remote.c \ + mi/Gfind_dynamic_proc_info.c \ + mi/Gget_accessors.c \ + mi/Gget_fpreg.c \ + mi/Gget_proc_info_by_ip.c \ + mi/Gget_proc_name.c \ + mi/Gget_reg.c \ + mi/Gput_dynamic_unwind_info.c \ + mi/Gset_cache_size.c \ + mi/Gset_caching_policy.c \ + mi/Gset_fpreg.c \ + mi/Gset_iterate_phdr_function.c \ + mi/Gset_reg.c \ + mi/Gget_elf_filename.c if SUPPORT_CXX_EXCEPTIONS -libunwind_la_SOURCES_local_unwind = \ - unwind/Backtrace.c unwind/DeleteException.c \ - unwind/FindEnclosingFunction.c unwind/ForcedUnwind.c \ - unwind/GetBSP.c unwind/GetCFA.c unwind/GetDataRelBase.c \ - unwind/GetGR.c unwind/GetIP.c unwind/GetLanguageSpecificData.c \ - unwind/GetRegionStart.c unwind/GetTextRelBase.c \ - unwind/RaiseException.c unwind/Resume.c \ - unwind/Resume_or_Rethrow.c unwind/SetGR.c unwind/SetIP.c \ - unwind/GetIPInfo.c +libunwind_la_SOURCES_local_unwind = \ + unwind/Backtrace.c \ + unwind/DeleteException.c \ + unwind/FindEnclosingFunction.c \ + unwind/ForcedUnwind.c \ + unwind/GetBSP.c \ + unwind/GetCFA.c \ + unwind/GetDataRelBase.c \ + unwind/GetGR.c \ + unwind/GetIP.c \ + unwind/GetIPInfo.c \ + unwind/GetLanguageSpecificData.c \ + unwind/GetRegionStart.c \ + unwind/GetTextRelBase.c \ + unwind/RaiseException.c \ + unwind/Resume.c \ + unwind/Resume_or_Rethrow.c \ + unwind/SetGR.c \ + unwind/SetIP.c # _ReadULEB()/_ReadSLEB() are needed for Intel C++ 8.0 compatibility -libunwind_la_SOURCES_os_linux_local = mi/_ReadULEB.c mi/_ReadSLEB.c -endif +libunwind_la_SOURCES_os_linux_local = \ + mi/_ReadULEB.c \ + mi/_ReadSLEB.c +endif SUPPORT_CXX_EXCEPTIONS # List of arch-independent files needed by local-only library (libunwind): -libunwind_la_SOURCES_local_nounwind = \ - $(libunwind_la_SOURCES_os_local) \ - mi/backtrace.c \ - mi/dyn-cancel.c mi/dyn-info-list.c mi/dyn-register.c \ - mi/Ldyn-extract.c mi/Lfind_dynamic_proc_info.c \ - mi/Lget_accessors.c \ - mi/Lget_proc_info_by_ip.c mi/Lget_proc_name.c \ - mi/Lput_dynamic_unwind_info.c mi/Ldestroy_addr_space.c \ - mi/Lget_reg.c mi/Lset_reg.c \ - mi/Lget_fpreg.c mi/Lset_fpreg.c \ - mi/Lset_caching_policy.c \ - mi/Lset_cache_size.c - -libunwind_la_SOURCES_local = \ - $(libunwind_la_SOURCES_local_nounwind) \ +libunwind_la_SOURCES_local_nounwind = \ + $(libunwind_la_SOURCES_os_local) \ + mi/backtrace.c \ + mi/dyn-cancel.c \ + mi/dyn-info-list.c \ + mi/dyn-register.c \ + mi/Laddress_validator.c \ + mi/Ldestroy_addr_space.c \ + mi/Ldyn-extract.c \ + mi/Lfind_dynamic_proc_info.c \ + mi/Lget_accessors.c \ + mi/Lget_fpreg.c mi/Lset_fpreg.c \ + mi/Lget_proc_info_by_ip.c \ + mi/Lget_proc_name.c \ + mi/Lget_reg.c \ + mi/Lput_dynamic_unwind_info.c \ + mi/Lset_cache_size.c \ + mi/Lset_caching_policy.c \ + mi/Lset_iterate_phdr_function.c \ + mi/Lset_reg.c \ + mi/Lget_elf_filename.c + +libunwind_la_SOURCES_local = \ + $(libunwind_la_SOURCES_local_nounwind) \ $(libunwind_la_SOURCES_local_unwind) noinst_HEADERS += os-linux.h -libunwind_la_SOURCES_os_linux = os-linux.c dl-iterate-phdr.c - -libunwind_la_SOURCES_os_hpux = os-hpux.c - -libunwind_la_SOURCES_os_freebsd = os-freebsd.c - -libunwind_la_SOURCES_os_qnx = os-qnx.c - -libunwind_la_SOURCES_os_solaris = os-solaris.c libunwind_dwarf_common_la_SOURCES = dwarf/global.c -libunwind_dwarf_local_la_SOURCES = \ - dwarf/Lexpr.c dwarf/Lfde.c dwarf/Lparser.c dwarf/Lpe.c \ - dwarf/Lfind_proc_info-lsb.c \ - dwarf/Lfind_unwind_table.c \ - dwarf/Lget_proc_info_in_range.c +libunwind_dwarf_local_la_SOURCES = \ + dwarf/Lexpr.c \ + dwarf/Lfde.c \ + dwarf/Lfind_proc_info-lsb.c \ + dwarf/Lfind_unwind_table.c \ + dwarf/Lget_proc_info_in_range.c \ + dwarf/Lparser.c \ + dwarf/Lpe.c libunwind_dwarf_local_la_LIBADD = libunwind-dwarf-common.la -libunwind_dwarf_generic_la_SOURCES = \ - dwarf/Gexpr.c dwarf/Gfde.c dwarf/Gparser.c dwarf/Gpe.c \ - dwarf/Gfind_proc_info-lsb.c \ - dwarf/Gfind_unwind_table.c \ - dwarf/Gget_proc_info_in_range.c +libunwind_dwarf_generic_la_SOURCES = \ + dwarf/Gexpr.c \ + dwarf/Gfde.c \ + dwarf/Gfind_proc_info-lsb.c \ + dwarf/Gfind_unwind_table.c \ + dwarf/Gget_proc_info_in_range.c \ + dwarf/Gparser.c \ + dwarf/Gpe.c libunwind_dwarf_generic_la_LIBADD = libunwind-dwarf-common.la if USE_DWARF @@ -193,354 +342,878 @@ libunwind_elf32_la_LIBADD = $(LIBLZMA) $(LIBZ) libunwind_elf64_la_LIBADD = $(LIBLZMA) $(LIBZ) libunwind_elfxx_la_LIBADD = $(LIBLZMA) $(LIBZ) -noinst_LTLIBRARIES += $(LIBUNWIND_ELF) -libunwind_la_LIBADD += $(LIBUNWIND_ELF) +noinst_LTLIBRARIES += $(libunwind_elf_libs) +libunwind_la_LIBADD += $(libunwind_elf_libs) -# The list of files that go into libunwind and libunwind-aarch64: -noinst_HEADERS += aarch64/init.h aarch64/offsets.h aarch64/unwind_i.h -libunwind_la_SOURCES_aarch64_common = $(libunwind_la_SOURCES_common) \ - aarch64/is_fpreg.c aarch64/regname.c +if OS_LINUX + libunwind_la_SOURCES_os = os-linux.c dl-iterate-phdr.c + libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_linux_local) + libunwind_la_SOURCES_aarch64_os = aarch64/Gos-linux.c + libunwind_la_SOURCES_aarch64_os_local = aarch64/Los-linux.c + libunwind_la_SOURCES_arm_os = arm/Gos-linux.c + libunwind_la_SOURCES_arm_os_local = arm/Los-linux.c + libunwind_la_SOURCES_x86_os = x86/Gos-linux.c + libunwind_la_SOURCES_x86_os_local = x86/Los-linux.c x86/getcontext-linux.S + libunwind_la_SOURCES_x86_64_os = x86_64/Gos-linux.c + libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-linux.c + libunwind_coredump_la_SOURCES += coredump/_UCD_access_reg_linux.c + libunwind_coredump_la_SOURCES += coredump/_UCD_get_threadinfo_prstatus.c + libunwind_coredump_la_SOURCES += coredump/_UCD_get_mapinfo_linux.c +endif -# The list of files that go into libunwind: -libunwind_la_SOURCES_aarch64 = $(libunwind_la_SOURCES_aarch64_common) \ - $(libunwind_la_SOURCES_local) \ - aarch64/Lapply_reg_state.c aarch64/Lreg_states_iterate.c \ - aarch64/Lcreate_addr_space.c aarch64/Lget_proc_info.c \ - aarch64/Lget_save_loc.c aarch64/Lglobal.c aarch64/Linit.c \ - aarch64/Linit_local.c aarch64/Linit_remote.c \ - aarch64/Lis_signal_frame.c aarch64/Lregs.c aarch64/Lresume.c \ - aarch64/Lstash_frame.c aarch64/Lstep.c aarch64/Ltrace.c \ - aarch64/getcontext.S - -libunwind_aarch64_la_SOURCES_aarch64 = $(libunwind_la_SOURCES_aarch64_common) \ - $(libunwind_la_SOURCES_generic) \ - aarch64/Gapply_reg_state.c aarch64/Greg_states_iterate.c \ - aarch64/Gcreate_addr_space.c aarch64/Gget_proc_info.c \ - aarch64/Gget_save_loc.c aarch64/Gglobal.c aarch64/Ginit.c \ - aarch64/Ginit_local.c aarch64/Ginit_remote.c \ - aarch64/Gis_signal_frame.c aarch64/Gregs.c aarch64/Gresume.c \ - aarch64/Gstash_frame.c aarch64/Gstep.c aarch64/Gtrace.c +if OS_HPUX + libunwind_la_SOURCES_os = os-hpux.c + libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_hpux_local) +endif -# The list of files that go into libunwind and libunwind-arm: -noinst_HEADERS += arm/init.h arm/offsets.h arm/unwind_i.h -libunwind_la_SOURCES_arm_common = $(libunwind_la_SOURCES_common) \ - arm/is_fpreg.c arm/regname.c +if OS_FREEBSD + libunwind_la_SOURCES_os = os-freebsd.c + libunwind_la_SOURCES_aarch64_os = aarch64/Gos-freebsd.c + libunwind_la_SOURCES_aarch64_os_local = aarch64/Los-freebsd.c aarch64/setcontext.S + libunwind_la_SOURCES_arm_os = arm/Gos-freebsd.c + libunwind_la_SOURCES_arm_os_local = arm/Los-freebsd.c + libunwind_la_SOURCES_x86_os = x86/Gos-freebsd.c + libunwind_la_SOURCES_x86_os_local = x86/Los-freebsd.c x86/getcontext-freebsd.S + libunwind_la_SOURCES_x86_64_os = x86_64/Gos-freebsd.c + libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-freebsd.c + libunwind_coredump_la_SOURCES += coredump/_UCD_access_reg_freebsd.c + libunwind_coredump_la_SOURCES += coredump/_UCD_get_threadinfo_prstatus.c + libunwind_coredump_la_SOURCES += coredump/_UCD_get_mapinfo_generic.c +endif -# The list of files that go into libunwind: -libunwind_la_SOURCES_arm = $(libunwind_la_SOURCES_arm_common) \ - $(libunwind_la_SOURCES_arm_os_local) \ - $(libunwind_la_SOURCES_local) \ - arm/getcontext.S \ - arm/Lapply_reg_state.c arm/Lreg_states_iterate.c \ - arm/Lcreate_addr_space.c arm/Lget_proc_info.c arm/Lget_save_loc.c \ - arm/Lglobal.c arm/Linit.c arm/Linit_local.c arm/Linit_remote.c \ - arm/Lregs.c arm/Lresume.c arm/Lstep.c \ - arm/Lex_tables.c arm/Lstash_frame.c arm/Ltrace.c +if OS_SOLARIS + libunwind_la_SOURCES_os = os-solaris.c + libunwind_la_SOURCES_x86_64_os = x86_64/Gos-solaris.c + libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-solaris.c +endif -# The list of files that go into libunwind-arm: -libunwind_arm_la_SOURCES_arm = $(libunwind_la_SOURCES_arm_common) \ - $(libunwind_la_SOURCES_arm_os) \ - $(libunwind_la_SOURCES_generic) \ - arm/Gapply_reg_state.c arm/Greg_states_iterate.c \ - arm/Gcreate_addr_space.c arm/Gget_proc_info.c arm/Gget_save_loc.c \ - arm/Gglobal.c arm/Ginit.c arm/Ginit_local.c arm/Ginit_remote.c \ - arm/Gregs.c arm/Gresume.c arm/Gstep.c \ - arm/Gex_tables.c arm/Gstash_frame.c arm/Gtrace.c +if OS_QNX + libunwind_la_SOURCES_os = os-qnx.c + libunwind_la_SOURCES_aarch64_os = aarch64/Gos-qnx.c + libunwind_la_SOURCES_aarch64_os_local = aarch64/Los-qnx.c + libunwind_la_SOURCES_arm_os = arm/Gos-other.c + libunwind_la_SOURCES_arm_os_local = arm/Los-other.c + libunwind_la_SOURCES_x86_64_os = x86_64/Gos-qnx.c + libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-qnx.c + libunwind_coredump_la_SOURCES += coredump/_UCD_access_reg_qnx.c + libunwind_coredump_la_SOURCES += coredump/_UCD_get_threadinfo_prstatus.c + libunwind_coredump_la_SOURCES += coredump/_UCD_get_mapinfo_qnx.c +endif -# The list of files that go both into libunwind and libunwind-ia64: -noinst_HEADERS += ia64/init.h ia64/offsets.h ia64/regs.h \ - ia64/ucontext_i.h ia64/unwind_decoder.h ia64/unwind_i.h -libunwind_la_SOURCES_ia64_common = $(libunwind_la_SOURCES_common) \ - ia64/regname.c -libunwind_la_EXTRAS_ia64 = ia64/mk_cursor_i ia64/mk_Lcursor_i.c \ - ia64/mk_Gcursor_i.c +### target AArch64: +# The list of files that go into libunwind and libunwind-aarch64: +noinst_HEADERS += aarch64/init.h aarch64/ucontext_i.h aarch64/unwind_i.h +libunwind_la_SOURCES_aarch64_common = \ + $(libunwind_la_SOURCES_common) \ + aarch64/is_fpreg.c \ + aarch64/regname.c # The list of files that go into libunwind: -libunwind_la_SOURCES_ia64 = $(libunwind_la_SOURCES_ia64_common) \ - $(libunwind_la_SOURCES_local) \ - \ - ia64/dyn_info_list.S ia64/getcontext.S \ - \ - ia64/Lapply_reg_state.c ia64/Lreg_states_iterate.c \ - ia64/Lcreate_addr_space.c ia64/Lget_proc_info.c ia64/Lget_save_loc.c \ - ia64/Lglobal.c ia64/Linit.c ia64/Linit_local.c ia64/Linit_remote.c \ - ia64/Linstall_cursor.S ia64/Lis_signal_frame.c ia64/Lparser.c \ - ia64/Lrbs.c ia64/Lregs.c ia64/Lresume.c ia64/Lscript.c ia64/Lstep.c \ - ia64/Ltables.c ia64/Lfind_unwind_table.c +if ARCH_AARCH64 +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_aarch64_common) \ + $(libunwind_la_SOURCES_local) \ + $(libunwind_la_SOURCES_aarch64_os_local) \ + aarch64/getcontext.S \ + aarch64/Lapply_reg_state.c \ + aarch64/Lcreate_addr_space.c \ + aarch64/Lget_proc_info.c \ + aarch64/Lget_save_loc.c \ + aarch64/Lglobal.c \ + aarch64/Linit.c \ + aarch64/Linit_local.c \ + aarch64/Linit_remote.c \ + aarch64/Lis_signal_frame.c \ + aarch64/Lregs.c \ + aarch64/Lreg_states_iterate.c \ + aarch64/Lresume.c \ + aarch64/Lstash_frame.c \ + aarch64/Lstep.c \ + aarch64/Ltrace.c + +libunwind_setjmp_la_SOURCES += \ + aarch64/longjmp.S \ + aarch64/siglongjmp.S +endif ARCH_AARCH64 + +libunwind_aarch64_la_SOURCES = \ + $(libunwind_la_SOURCES_aarch64_common) \ + $(libunwind_la_SOURCES_generic) \ + $(libunwind_la_SOURCES_aarch64_os) \ + aarch64/Gapply_reg_state.c \ + aarch64/Gcreate_addr_space.c \ + aarch64/Gget_proc_info.c \ + aarch64/Gget_save_loc.c \ + aarch64/Gglobal.c \ + aarch64/Ginit.c \ + aarch64/Ginit_local.c \ + aarch64/Ginit_remote.c \ + aarch64/Gis_signal_frame.c \ + aarch64/Gregs.c \ + aarch64/Greg_states_iterate.c \ + aarch64/Gresume.c \ + aarch64/Gstash_frame.c \ + aarch64/Gstep.c \ + aarch64/Gtrace.c +libunwind_aarch64_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_aarch64_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf64.la \ + $(libunwind_libadd) + +### target ARMv7: +# The list of files that go into libunwind and libunwind-arm: +noinst_HEADERS += \ + arm/init.h \ + arm/offsets.h \ + arm/unwind_i.h +libunwind_la_SOURCES_arm_common = \ + $(libunwind_la_SOURCES_common) \ + arm/is_fpreg.c arm/regname.c -# The list of files that go into libunwind-ia64: -libunwind_ia64_la_SOURCES_ia64 = $(libunwind_la_SOURCES_ia64_common) \ - $(libunwind_la_SOURCES_generic) \ - ia64/Gapply_reg_state.c ia64/Greg_states_iterate.c \ - ia64/Gcreate_addr_space.c ia64/Gget_proc_info.c ia64/Gget_save_loc.c \ - ia64/Gglobal.c ia64/Ginit.c ia64/Ginit_local.c ia64/Ginit_remote.c \ - ia64/Ginstall_cursor.S ia64/Gis_signal_frame.c ia64/Gparser.c \ - ia64/Grbs.c ia64/Gregs.c ia64/Gresume.c ia64/Gscript.c ia64/Gstep.c \ - ia64/Gtables.c ia64/Gfind_unwind_table.c +# The list of files that go into libunwind: +if ARCH_ARM +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_arm_common) \ + $(libunwind_la_SOURCES_arm_os_local) \ + $(libunwind_la_SOURCES_local) \ + arm/getcontext.S \ + arm/Lapply_reg_state.c \ + arm/Lcreate_addr_space.c \ + arm/Lex_tables.c \ + arm/Lget_proc_info.c \ + arm/Lget_save_loc.c \ + arm/Lglobal.c arm/Linit.c \ + arm/Linit_local.c \ + arm/Linit_remote.c \ + arm/Lregs.c \ + arm/Lreg_states_iterate.c \ + arm/Lresume.c \ + arm/Lstash_frame.c \ + arm/Lstep.c \ + arm/Ltrace.c + +libunwind_setjmp_la_SOURCES += arm/siglongjmp.S +endif # ARCH_ARM +# The list of files that go into libunwind-arm: +libunwind_arm_la_SOURCES = \ + $(libunwind_la_SOURCES_arm_common) \ + $(libunwind_la_SOURCES_arm_os) \ + $(libunwind_la_SOURCES_generic) \ + arm/Gapply_reg_state.c \ + arm/Gcreate_addr_space.c \ + arm/Gex_tables.c \ + arm/Gget_proc_info.c \ + arm/Gget_save_loc.c \ + arm/Gglobal.c \ + arm/Ginit.c \ + arm/Ginit_local.c \ + arm/Ginit_remote.c \ + arm/Gregs.c \ + arm/Greg_states_iterate.c \ + arm/Gresume.c \ + arm/Gstash_frame.c \ + arm/Gstep.c \ + arm/Gtrace.c +libunwind_arm_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_arm_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf32.la \ + $(libunwind_libadd) + +### target HP-PA: # The list of files that go both into libunwind and libunwind-hppa: -noinst_HEADERS += hppa/init.h hppa/offsets.h hppa/unwind_i.h -libunwind_la_SOURCES_hppa_common = $(libunwind_la_SOURCES_common) \ +noinst_HEADERS += \ + hppa/init.h \ + hppa/offsets.h \ + hppa/unwind_i.h +libunwind_la_SOURCES_hppa_common = \ + $(libunwind_la_SOURCES_common) \ hppa/regname.c +if ARCH_HPPA # The list of files that go into libunwind: -libunwind_la_SOURCES_hppa = $(libunwind_la_SOURCES_hppa_common) \ - $(libunwind_la_SOURCES_local) \ - hppa/getcontext.S hppa/setcontext.S \ - hppa/Lapply_reg_state.c hppa/Lreg_states_iterate.c \ - hppa/Lcreate_addr_space.c hppa/Lget_save_loc.c hppa/Lglobal.c \ - hppa/Linit.c hppa/Linit_local.c hppa/Linit_remote.c \ - hppa/Lis_signal_frame.c hppa/Lget_proc_info.c hppa/Lregs.c \ - hppa/Lresume.c hppa/Lstep.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_hppa_common) \ + $(libunwind_la_SOURCES_local) \ + hppa/getcontext.S \ + hppa/Lapply_reg_state.c \ + hppa/Lcreate_addr_space.c \ + hppa/Lget_proc_info.c \ + hppa/Lget_save_loc.c \ + hppa/Lglobal.c \ + hppa/Linit.c \ + hppa/Linit_local.c \ + hppa/Linit_remote.c \ + hppa/Lis_signal_frame.c \ + hppa/Lregs.c \ + hppa/Lreg_states_iterate.c \ + hppa/Lresume.c \ + hppa/Lstep.c \ + hppa/setcontext.S + +libunwind_setjmp_la_SOURCES += hppa/siglongjmp.S +endif # ARCH_HPPA # The list of files that go into libunwind-hppa: -libunwind_hppa_la_SOURCES_hppa = $(libunwind_la_SOURCES_hppa_common) \ - $(libunwind_la_SOURCES_generic) \ - hppa/Gapply_reg_state.c hppa/Greg_states_iterate.c \ - hppa/Gcreate_addr_space.c hppa/Gget_save_loc.c hppa/Gglobal.c \ - hppa/Ginit.c hppa/Ginit_local.c hppa/Ginit_remote.c \ - hppa/Gis_signal_frame.c hppa/Gget_proc_info.c hppa/Gregs.c \ - hppa/Gresume.c hppa/Gstep.c - -# The list of files that go info libunwind and libunwind-mips: -noinst_HEADERS += mips/init.h mips/offsets.h mips/unwind_i.h -libunwind_la_SOURCES_mips_common = $(libunwind_la_SOURCES_common) \ - mips/is_fpreg.c mips/regname.c - -# The list of files that go into libunwind: -libunwind_la_SOURCES_mips = $(libunwind_la_SOURCES_mips_common) \ - $(libunwind_la_SOURCES_local) \ - mips/getcontext.S \ - mips/Lapply_reg_state.c mips/Lreg_states_iterate.c \ - mips/Lcreate_addr_space.c mips/Lget_proc_info.c mips/Lget_save_loc.c \ - mips/Lglobal.c mips/Linit.c mips/Linit_local.c mips/Linit_remote.c \ - mips/Lis_signal_frame.c mips/Lregs.c mips/Lresume.c mips/Lstep.c - -libunwind_mips_la_SOURCES_mips = $(libunwind_la_SOURCES_mips_common) \ - $(libunwind_la_SOURCES_generic) \ - mips/Gapply_reg_state.c mips/Greg_states_iterate.c \ - mips/Gcreate_addr_space.c mips/Gget_proc_info.c mips/Gget_save_loc.c \ - mips/Gglobal.c mips/Ginit.c mips/Ginit_local.c mips/Ginit_remote.c \ - mips/Gis_signal_frame.c mips/Gregs.c mips/Gresume.c mips/Gstep.c - -# The list of files that go info libunwind and libunwind-tilegx: -noinst_HEADERS += tilegx/init.h tilegx/offsets.h tilegx/unwind_i.h -libunwind_la_SOURCES_tilegx_common = $(libunwind_la_SOURCES_common) \ - tilegx/is_fpreg.c tilegx/regname.c - -# The list of files that go into libunwind: -libunwind_la_SOURCES_tilegx = $(libunwind_la_SOURCES_tilegx_common) \ - $(libunwind_la_SOURCES_local) \ - tilegx/getcontext.S \ - tilegx/Lapply_reg_state.c tilegx/Lreg_states_iterate.c \ - tilegx/Lcreate_addr_space.c tilegx/Lget_proc_info.c tilegx/Lget_save_loc.c \ - tilegx/Lglobal.c tilegx/Linit.c tilegx/Linit_local.c tilegx/Linit_remote.c \ - tilegx/Lis_signal_frame.c tilegx/Lregs.c tilegx/Lresume.c tilegx/Lstep.c - -libunwind_tilegx_la_SOURCES_tilegx = $(libunwind_la_SOURCES_tilegx_common) \ - $(libunwind_la_SOURCES_generic) \ - tilegx/Gapply_reg_state.c tilegx/Greg_states_iterate.c \ - tilegx/Gcreate_addr_space.c tilegx/Gget_proc_info.c tilegx/Gget_save_loc.c \ - tilegx/Gglobal.c tilegx/Ginit.c tilegx/Ginit_local.c tilegx/Ginit_remote.c \ - tilegx/Gis_signal_frame.c tilegx/Gregs.c tilegx/Gresume.c tilegx/Gstep.c +libunwind_hppa_la_SOURCES = \ + $(libunwind_la_SOURCES_hppa_common) \ + $(libunwind_la_SOURCES_generic) \ + hppa/Gapply_reg_state.c \ + hppa/Gcreate_addr_space.c \ + hppa/Gget_proc_info.c \ + hppa/Gget_save_loc.c \ + hppa/Gglobal.c \ + hppa/Ginit.c \ + hppa/Ginit_local.c \ + hppa/Ginit_remote.c \ + hppa/Gis_signal_frame.c \ + hppa/Gregs.c \ + hppa/Greg_states_iterate.c \ + hppa/Gresume.c \ + hppa/Gstep.c +libunwind_hppa_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_hppa_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf32.la \ + $(libunwind_libadd) + +### target IA-64: +# The list of files that go both into libunwind and libunwind-ia64: +noinst_HEADERS += \ + ia64/init.h \ + ia64/offsets.h \ + ia64/regs.h \ + ia64/ucontext_i.h \ + ia64/unwind_decoder.h \ + ia64/unwind_i.h +libunwind_la_SOURCES_ia64_common = \ + $(libunwind_la_SOURCES_common) \ + ia64/regname.c +libunwind_la_EXTRAS_ia64 = \ + ia64/mk_cursor_i \ + ia64/mk_Gcursor_i.c \ + ia64/mk_Lcursor_i.c -# The list of files that go info libunwind and libunwind-riscv: -noinst_HEADERS += riscv/init.h riscv/offsets.h riscv/unwind_i.h riscv/asm.h -libunwind_la_SOURCES_riscv_common = $(libunwind_la_SOURCES_common) \ - riscv/is_fpreg.c riscv/regname.c +if ARCH_IA64 +BUILT_SOURCES = Gcursor_i.h Lcursor_i.h +mk_Gcursor_i.s: $(srcdir)/ia64/mk_Gcursor_i.c + $(COMPILE) -S "$(srcdir)/ia64/mk_Gcursor_i.c" -o mk_Gcursor_i.s +mk_Lcursor_i.s: $(srcdir)/ia64/mk_Lcursor_i.c + $(COMPILE) -S "$(srcdir)/ia64/mk_Lcursor_i.c" -o mk_Lcursor_i.s +Gcursor_i.h: mk_Gcursor_i.s + "$(srcdir)/ia64/mk_cursor_i" mk_Gcursor_i.s > Gcursor_i.h +Lcursor_i.h: mk_Lcursor_i.s + "$(srcdir)/ia64/mk_cursor_i" mk_Lcursor_i.s > Lcursor_i.h # The list of files that go into libunwind: -libunwind_la_SOURCES_riscv = $(libunwind_la_SOURCES_riscv_common) \ - $(libunwind_la_SOURCES_local) \ - riscv/getcontext.S riscv/setcontext.S \ - riscv/Lapply_reg_state.c riscv/Lreg_states_iterate.c \ - riscv/Lcreate_addr_space.c riscv/Lget_proc_info.c riscv/Lget_save_loc.c \ - riscv/Lglobal.c riscv/Linit.c riscv/Linit_local.c riscv/Linit_remote.c \ - riscv/Lis_signal_frame.c riscv/Lregs.c riscv/Lresume.c riscv/Lstep.c - -libunwind_riscv_la_SOURCES_riscv = $(libunwind_la_SOURCES_riscv_common) \ - $(libunwind_la_SOURCES_generic) \ - riscv/Gapply_reg_state.c riscv/Greg_states_iterate.c \ - riscv/Gcreate_addr_space.c riscv/Gget_proc_info.c riscv/Gget_save_loc.c \ - riscv/Gglobal.c riscv/Ginit.c riscv/Ginit_local.c riscv/Ginit_remote.c \ - riscv/Gis_signal_frame.c riscv/Gregs.c riscv/Gresume.c riscv/Gstep.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_ia64_common) \ + $(libunwind_la_SOURCES_local) \ + ia64/dyn_info_list.S \ + ia64/getcontext.S \ + ia64/Lapply_reg_state.c \ + ia64/Lcreate_addr_space.c \ + ia64/Lfind_unwind_table.c \ + ia64/Lget_proc_info.c \ + ia64/Lget_save_loc.c \ + ia64/Lglobal.c \ + ia64/Linit.c \ + ia64/Linit_local.c \ + ia64/Linit_remote.c \ + ia64/Linstall_cursor.S \ + ia64/Lis_signal_frame.c \ + ia64/Lparser.c \ + ia64/Lrbs.c \ + ia64/Lregs.c \ + ia64/Lreg_states_iterate.c \ + ia64/Lresume.c \ + ia64/Lscript.c \ + ia64/Lstep.c \ + ia64/Ltables.c + +libunwind_setjmp_la_SOURCES += \ + ia64/longjmp.S \ + ia64/setjmp.S \ + ia64/siglongjmp.S \ + ia64/sigsetjmp.S +endif # ARCH_IA64 +# The list of files that go into libunwind-ia64: +libunwind_ia64_la_SOURCES = \ + $(libunwind_la_SOURCES_ia64_common) \ + $(libunwind_la_SOURCES_generic) \ + ia64/Gapply_reg_state.c \ + ia64/Gcreate_addr_space.c \ + ia64/Gfind_unwind_table.c \ + ia64/Gget_proc_info.c \ + ia64/Gget_save_loc.c \ + ia64/Gglobal.c \ + ia64/Ginit.c \ + ia64/Ginit_local.c \ + ia64/Ginit_remote.c \ + ia64/Ginstall_cursor.S \ + ia64/Gis_signal_frame.c \ + ia64/Gparser.c \ + ia64/Grbs.c \ + ia64/Gregs.c \ + ia64/Greg_states_iterate.c \ + ia64/Gresume.c \ + ia64/Gscript.c \ + ia64/Gstep.c \ + ia64/Gtables.c +libunwind_ia64_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_ia64_la_LIBADD = \ + libunwind-elf64.la \ + $(libunwind_libadd) + +### target LoongArch64: # The list of files that go info libunwind and libunwind-loongarch64: -noinst_HEADERS += loongarch64/init.h loongarch64/offsets.h loongarch64/unwind_i.h -libunwind_la_SOURCES_loongarch64_common = $(libunwind_la_SOURCES_common) \ - loongarch64/is_fpreg.c loongarch64/regname.c - -# The list of files that go into libunwind: -libunwind_la_SOURCES_loongarch64 = $(libunwind_la_SOURCES_loongarch64_common) \ - $(libunwind_la_SOURCES_local) \ - loongarch64/getcontext.S \ - loongarch64/Lapply_reg_state.c loongarch64/Lreg_states_iterate.c \ - loongarch64/Lcreate_addr_space.c loongarch64/Lget_proc_info.c \ - loongarch64/Lget_save_loc.c loongarch64/Lglobal.c loongarch64/Linit.c \ - loongarch64/Linit_local.c loongarch64/Linit_remote.c \ - loongarch64/Lis_signal_frame.c loongarch64/Lregs.c \ - loongarch64/Lresume.c loongarch64/Lstep.c - -libunwind_loongarch64_la_SOURCES_loongarch64 = $(libunwind_la_SOURCES_loongarch64_common) \ - $(libunwind_la_SOURCES_generic) \ - loongarch64/Gapply_reg_state.c loongarch64/Greg_states_iterate.c \ - loongarch64/Gcreate_addr_space.c loongarch64/Gget_proc_info.c \ - loongarch64/Gget_save_loc.c loongarch64/Gglobal.c loongarch64/Ginit.c \ - loongarch64/Ginit_local.c loongarch64/Ginit_remote.c \ - loongarch64/Gis_signal_frame.c loongarch64/Gregs.c \ - loongarch64/Gresume.c loongarch64/Gstep.c - -# The list of files that go both into libunwind and libunwind-x86: -noinst_HEADERS += x86/init.h x86/offsets.h x86/unwind_i.h -libunwind_la_SOURCES_x86_common = $(libunwind_la_SOURCES_common) \ - x86/is_fpreg.c x86/regname.c +noinst_HEADERS += \ + loongarch64/init.h \ + loongarch64/offsets.h \ + loongarch64/unwind_i.h +libunwind_la_SOURCES_loongarch64_common = \ + $(libunwind_la_SOURCES_common) \ + loongarch64/is_fpreg.c \ + loongarch64/regname.c +if ARCH_LOONGARCH64 # The list of files that go into libunwind: -libunwind_la_SOURCES_x86 = $(libunwind_la_SOURCES_x86_common) \ - $(libunwind_la_SOURCES_x86_os_local) \ - $(libunwind_la_SOURCES_local) \ - x86/Lapply_reg_state.c x86/Lreg_states_iterate.c \ - x86/Lcreate_addr_space.c x86/Lget_save_loc.c x86/Lglobal.c \ - x86/Linit.c x86/Linit_local.c x86/Linit_remote.c \ - x86/Lget_proc_info.c x86/Lregs.c \ - x86/Lresume.c x86/Lstep.c - -# The list of files that go into libunwind-x86: -libunwind_x86_la_SOURCES_x86 = $(libunwind_la_SOURCES_x86_common) \ - $(libunwind_la_SOURCES_x86_os) \ - $(libunwind_la_SOURCES_generic) \ - x86/Gapply_reg_state.c x86/Greg_states_iterate.c \ - x86/Gcreate_addr_space.c x86/Gget_save_loc.c x86/Gglobal.c \ - x86/Ginit.c x86/Ginit_local.c x86/Ginit_remote.c \ - x86/Gget_proc_info.c x86/Gregs.c \ - x86/Gresume.c x86/Gstep.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_loongarch64_common) \ + $(libunwind_la_SOURCES_local) \ + loongarch64/getcontext.S \ + loongarch64/Lapply_reg_state.c \ + loongarch64/Lcreate_addr_space.c \ + loongarch64/Lget_proc_info.c \ + loongarch64/Lget_save_loc.c \ + loongarch64/Lglobal.c \ + loongarch64/Linit.c \ + loongarch64/Linit_local.c \ + loongarch64/Linit_remote.c \ + loongarch64/Lis_signal_frame.c \ + loongarch64/Lregs.c \ + loongarch64/Lreg_states_iterate.c \ + loongarch64/Lresume.c \ + loongarch64/Lstep.c + +libunwind_setjmp_la_SOURCES += \ + loongarch64/siglongjmp.S +endif # ARCH_LOONGARCH64 -# The list of files that go both into libunwind and libunwind-x86_64: -noinst_HEADERS += x86_64/offsets.h \ - x86_64/init.h x86_64/unwind_i.h x86_64/ucontext_i.h -libunwind_la_SOURCES_x86_64_common = $(libunwind_la_SOURCES_common) \ - x86_64/is_fpreg.c x86_64/regname.c +libunwind_loongarch64_la_SOURCES = \ + $(libunwind_la_SOURCES_loongarch64_common) \ + $(libunwind_la_SOURCES_generic) \ + loongarch64/Gapply_reg_state.c \ + loongarch64/Gcreate_addr_space.c \ + loongarch64/Gget_proc_info.c \ + loongarch64/Gget_save_loc.c \ + loongarch64/Gglobal.c \ + loongarch64/Ginit.c \ + loongarch64/Ginit_local.c \ + loongarch64/Ginit_remote.c \ + loongarch64/Gis_signal_frame.c \ + loongarch64/Gregs.c \ + loongarch64/Greg_states_iterate.c \ + loongarch64/Gresume.c \ + loongarch64/Gstep.c +libunwind_loongarch64_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_loongarch64_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf64.la \ + $(libunwind_libadd) + +### target MIPS: +# The list of files that go info libunwind and libunwind-mips: +noinst_HEADERS += \ + mips/init.h \ + mips/offsets.h \ + mips/unwind_i.h +libunwind_la_SOURCES_mips_common = \ + $(libunwind_la_SOURCES_common) \ + mips/is_fpreg.c \ + mips/regname.c +if ARCH_MIPS # The list of files that go into libunwind: -libunwind_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \ - $(libunwind_la_SOURCES_x86_64_os_local) \ - $(libunwind_la_SOURCES_local) \ - x86_64/setcontext.S \ - x86_64/Lapply_reg_state.c x86_64/Lreg_states_iterate.c \ - x86_64/Lcreate_addr_space.c x86_64/Lget_save_loc.c x86_64/Lglobal.c \ - x86_64/Linit.c x86_64/Linit_local.c x86_64/Linit_remote.c \ - x86_64/Lget_proc_info.c x86_64/Lregs.c x86_64/Lresume.c \ - x86_64/Lstash_frame.c x86_64/Lstep.c x86_64/Ltrace.c x86_64/getcontext.S - -# The list of files that go into libunwind-x86_64: -libunwind_x86_64_la_SOURCES_x86_64 = $(libunwind_la_SOURCES_x86_64_common) \ - $(libunwind_la_SOURCES_x86_64_os) \ - $(libunwind_la_SOURCES_generic) \ - x86_64/Gapply_reg_state.c x86_64/Greg_states_iterate.c \ - x86_64/Gcreate_addr_space.c x86_64/Gget_save_loc.c x86_64/Gglobal.c \ - x86_64/Ginit.c x86_64/Ginit_local.c x86_64/Ginit_remote.c \ - x86_64/Gget_proc_info.c x86_64/Gregs.c x86_64/Gresume.c \ - x86_64/Gstash_frame.c x86_64/Gstep.c x86_64/Gtrace.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_mips_common) \ + $(libunwind_la_SOURCES_local) \ + mips/getcontext.S \ + mips/Lapply_reg_state.c \ + mips/Lcreate_addr_space.c \ + mips/Lget_proc_info.c \ + mips/Lget_save_loc.c \ + mips/Lglobal.c \ + mips/Linit.c \ + mips/Linit_local.c \ + mips/Linit_remote.c \ + mips/Lis_signal_frame.c \ + mips/Lregs.c \ + mips/Lreg_states_iterate.c \ + mips/Lresume.c \ + mips/Lstep.c + +libunwind_setjmp_la_SOURCES += mips/siglongjmp.S +endif # ARCH_MIPS +libunwind_mips_la_SOURCES = \ + $(libunwind_la_SOURCES_mips_common) \ + $(libunwind_la_SOURCES_generic) \ + mips/Gapply_reg_state.c \ + mips/Gcreate_addr_space.c \ + mips/Gget_proc_info.c \ + mips/Gget_save_loc.c \ + mips/Gglobal.c \ + mips/Ginit.c \ + mips/Ginit_local.c \ + mips/Ginit_remote.c \ + mips/Gis_signal_frame.c \ + mips/Gregs.c \ + mips/Greg_states_iterate.c \ + mips/Gresume.c \ + mips/Gstep.c +libunwind_mips_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_mips_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elfxx.la \ + $(libunwind_libadd) + +### target PowerPC: # The list of local files that go to Power 64 and 32: -libunwind_la_SOURCES_ppc = \ - ppc/Lget_proc_info.c ppc/Lget_save_loc.c ppc/Linit_local.c \ - ppc/Linit_remote.c ppc/Lis_signal_frame.c +libunwind_la_SOURCES_ppc = \ + ppc/Lget_proc_info.c \ + ppc/Lget_save_loc.c \ + ppc/Linit_local.c \ + ppc/Linit_remote.c \ + ppc/Lis_signal_frame.c # The list of generic files that go to Power 64 and 32: -libunwind_ppc_la_SOURCES_ppc_generic = \ - ppc/Gget_proc_info.c ppc/Gget_save_loc.c ppc/Ginit_local.c \ - ppc/Ginit_remote.c ppc/Gis_signal_frame.c +libunwind_ppc_la_SOURCES_ppc_generic = \ + ppc/Gget_proc_info.c \ + ppc/Gget_save_loc.c \ + ppc/Ginit_local.c \ + ppc/Ginit_remote.c \ + ppc/Gis_signal_frame.c + # The list of files that go both into libunwind and libunwind-ppc32: -noinst_HEADERS += ppc32/init.h ppc32/unwind_i.h ppc32/ucontext_i.h -libunwind_la_SOURCES_ppc32_common = $(libunwind_la_SOURCES_common) \ - ppc32/is_fpreg.c ppc32/regname.c ppc32/get_func_addr.c +noinst_HEADERS += \ + ppc32/init.h \ + ppc32/unwind_i.h \ + ppc32/ucontext_i.h +libunwind_la_SOURCES_ppc32_common = \ + $(libunwind_la_SOURCES_common) \ + ppc32/get_func_addr.c \ + ppc32/is_fpreg.c \ + ppc32/regname.c +if ARCH_PPC32 # The list of files that go into libunwind: -libunwind_la_SOURCES_ppc32 = $(libunwind_la_SOURCES_ppc32_common) \ - $(libunwind_la_SOURCES_local) \ - $(libunwind_la_SOURCES_ppc) \ - ppc32/Lapply_reg_state.c ppc32/Lreg_states_iterate.c \ - ppc32/Lcreate_addr_space.c \ - ppc32/Lglobal.c ppc32/Linit.c \ - ppc32/Lregs.c ppc32/Lresume.c ppc32/Lstep.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_ppc32_common) \ + $(libunwind_la_SOURCES_local) \ + $(libunwind_la_SOURCES_ppc) \ + ppc32/Lapply_reg_state.c \ + ppc32/Lcreate_addr_space.c \ + ppc32/Lglobal.c \ + ppc32/Linit.c \ + ppc32/Lregs.c \ + ppc32/Lreg_states_iterate.c \ + ppc32/Lresume.c \ + ppc32/Lstep.c + +libunwind_setjmp_la_SOURCES += \ + ppc/longjmp.S \ + ppc/siglongjmp.S +endif # ARCH_PPC32 # The list of files that go into libunwind-ppc32: -libunwind_ppc32_la_SOURCES_ppc32 = $(libunwind_la_SOURCES_ppc32_common) \ - $(libunwind_la_SOURCES_generic) \ - $(libunwind_ppc_la_SOURCES_ppc_generic) \ - ppc32/Gapply_reg_state.c ppc32/Greg_states_iterate.c \ - ppc32/Gcreate_addr_space.c \ - ppc32/Gglobal.c ppc32/Ginit.c \ - ppc32/Gregs.c ppc32/Gresume.c ppc32/Gstep.c +libunwind_ppc32_la_SOURCES = \ + $(libunwind_la_SOURCES_ppc32_common) \ + $(libunwind_la_SOURCES_generic) \ + $(libunwind_ppc_la_SOURCES_ppc_generic)\ + ppc32/Gapply_reg_state.c \ + ppc32/Gcreate_addr_space.c \ + ppc32/Gglobal.c \ + ppc32/Ginit.c \ + ppc32/Gregs.c \ + ppc32/Greg_states_iterate.c \ + ppc32/Gresume.c \ + ppc32/Gstep.c +libunwind_ppc32_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_ppc32_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf32.la \ + $(libunwind_libadd) # The list of files that go both into libunwind and libunwind-ppc64: -noinst_HEADERS += ppc64/init.h ppc64/unwind_i.h ppc64/ucontext_i.h -libunwind_la_SOURCES_ppc64_common = $(libunwind_la_SOURCES_common) \ - ppc64/is_fpreg.c ppc64/regname.c ppc64/get_func_addr.c +noinst_HEADERS += \ + ppc64/init.h \ + ppc64/unwind_i.h \ + ppc64/ucontext_i.h +libunwind_la_SOURCES_ppc64_common = \ + $(libunwind_la_SOURCES_common) \ + ppc64/get_func_addr.c \ + ppc64/is_fpreg.c \ + ppc64/regname.c +if ARCH_PPC64 # The list of files that go into libunwind: -libunwind_la_SOURCES_ppc64 = $(libunwind_la_SOURCES_ppc64_common) \ - $(libunwind_la_SOURCES_local) \ - $(libunwind_la_SOURCES_ppc) \ - ppc64/Lapply_reg_state.c ppc64/Lreg_states_iterate.c \ - ppc64/Lcreate_addr_space.c \ - ppc64/Lglobal.c ppc64/Linit.c \ - ppc64/Lregs.c ppc64/Lresume.c ppc64/Lstep.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_ppc64_common) \ + $(libunwind_la_SOURCES_local) \ + $(libunwind_la_SOURCES_ppc) \ + ppc64/Lapply_reg_state.c \ + ppc64/Lcreate_addr_space.c \ + ppc64/Lglobal.c \ + ppc64/Linit.c \ + ppc64/Lregs.c \ + ppc64/Lreg_states_iterate.c \ + ppc64/Lresume.c \ + ppc64/Lstep.c +libunwind_setjmp_la_SOURCES += \ + ppc/longjmp.S \ + ppc/siglongjmp.S +endif # ARCH_PPC64 # The list of files that go into libunwind-ppc64: -libunwind_ppc64_la_SOURCES_ppc64 = $(libunwind_la_SOURCES_ppc64_common) \ - $(libunwind_la_SOURCES_generic) \ - $(libunwind_ppc_la_SOURCES_ppc_generic) \ - ppc64/Gapply_reg_state.c ppc64/Greg_states_iterate.c \ - ppc64/Gcreate_addr_space.c \ - ppc64/Gglobal.c ppc64/Ginit.c \ - ppc64/Gregs.c ppc64/Gresume.c ppc64/Gstep.c - -# The list of files that go into libunwind and libunwind-sh: -noinst_HEADERS += sh/init.h sh/offsets.h sh/unwind_i.h -libunwind_la_SOURCES_sh_common = $(libunwind_la_SOURCES_common) \ - sh/is_fpreg.c sh/regname.c +libunwind_ppc64_la_SOURCES = \ + $(libunwind_la_SOURCES_ppc64_common) \ + $(libunwind_la_SOURCES_generic) \ + $(libunwind_ppc_la_SOURCES_ppc_generic)\ + ppc64/Gapply_reg_state.c \ + ppc64/Gcreate_addr_space.c \ + ppc64/Gglobal.c \ + ppc64/Ginit.c \ + ppc64/Gregs.c \ + ppc64/Greg_states_iterate.c \ + ppc64/Gresume.c \ + ppc64/Gstep.c +libunwind_ppc64_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_ppc64_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf64.la \ + $(libunwind_libadd) + +### target RISC-V: +# The list of files that go info libunwind and libunwind-riscv: +noinst_HEADERS += \ + riscv/asm.h \ + riscv/init.h \ + riscv/offsets.h \ + riscv/unwind_i.h +libunwind_la_SOURCES_riscv_common = \ + $(libunwind_la_SOURCES_common) \ + riscv/is_fpreg.c \ + riscv/regname.c +if ARCH_RISCV # The list of files that go into libunwind: -libunwind_la_SOURCES_sh = $(libunwind_la_SOURCES_sh_common) \ - $(libunwind_la_SOURCES_local) \ - sh/Lapply_reg_state.c sh/Lreg_states_iterate.c \ - sh/Lcreate_addr_space.c sh/Lget_proc_info.c sh/Lget_save_loc.c \ - sh/Lglobal.c sh/Linit.c sh/Linit_local.c sh/Linit_remote.c \ - sh/Lis_signal_frame.c sh/Lregs.c sh/Lresume.c sh/Lstep.c - -libunwind_sh_la_SOURCES_sh = $(libunwind_la_SOURCES_sh_common) \ - $(libunwind_la_SOURCES_generic) \ - sh/Gapply_reg_state.c sh/Greg_states_iterate.c \ - sh/Gcreate_addr_space.c sh/Gget_proc_info.c sh/Gget_save_loc.c \ - sh/Gglobal.c sh/Ginit.c sh/Ginit_local.c sh/Ginit_remote.c \ - sh/Gis_signal_frame.c sh/Gregs.c sh/Gresume.c sh/Gstep.c +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_riscv_common) \ + $(libunwind_la_SOURCES_local) \ + riscv/getcontext.S \ + riscv/Lapply_reg_state.c \ + riscv/Lcreate_addr_space.c \ + riscv/Lget_proc_info.c \ + riscv/Lget_save_loc.c \ + riscv/Lglobal.c \ + riscv/Linit.c \ + riscv/Linit_local.c \ + riscv/Linit_remote.c \ + riscv/Lis_signal_frame.c \ + riscv/Lregs.c \ + riscv/Lreg_states_iterate.c \ + riscv/Lresume.c \ + riscv/Lstep.c \ + riscv/setcontext.S + +libunwind_setjmp_la_SOURCES += \ + riscv/siglongjmp.S +endif # ARCH_RISCV +libunwind_riscv_la_SOURCES = \ + $(libunwind_la_SOURCES_riscv_common) \ + $(libunwind_la_SOURCES_generic) \ + riscv/Gapply_reg_state.c \ + riscv/Gcreate_addr_space.c \ + riscv/Gget_proc_info.c \ + riscv/Gget_save_loc.c \ + riscv/Gglobal.c \ + riscv/Ginit.c \ + riscv/Ginit_local.c \ + riscv/Ginit_remote.c \ + riscv/Gis_signal_frame.c \ + riscv/Gregs.c \ + riscv/Greg_states_iterate.c \ + riscv/Gresume.c \ + riscv/Gstep.c + +libunwind_riscv_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_riscv_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf64.la \ + $(libunwind_libadd) + +### target s390x: # The list of files that go both into libunwind and libunwind-s390x: -noinst_HEADERS += s390x/init.h s390x/unwind_i.h -libunwind_la_SOURCES_s390x_common = $(libunwind_la_SOURCES_common) \ - s390x/is_fpreg.c s390x/regname.c +noinst_HEADERS += \ + s390x/init.h \ + s390x/unwind_i.h +libunwind_la_SOURCES_s390x_common = \ + $(libunwind_la_SOURCES_common) \ + s390x/is_fpreg.c \ + s390x/regname.c +if ARCH_S390X # The list of files that go into libunwind: -libunwind_la_SOURCES_s390x = $(libunwind_la_SOURCES_s390x_common) \ - $(libunwind_la_SOURCES_local) \ - s390x/Lapply_reg_state.c s390x/Lreg_states_iterate.c \ - s390x/Lcreate_addr_space.c s390x/Lget_save_loc.c s390x/Lglobal.c \ - s390x/Linit.c s390x/Linit_local.c s390x/Linit_remote.c \ - s390x/Lget_proc_info.c s390x/Lregs.c s390x/Lresume.c \ - s390x/Lis_signal_frame.c s390x/Lstep.c \ - s390x/getcontext.S s390x/setcontext.S +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_s390x_common) \ + $(libunwind_la_SOURCES_local) \ + s390x/getcontext.S \ + s390x/Lapply_reg_state.c \ + s390x/Lcreate_addr_space.c \ + s390x/Lget_proc_info.c \ + s390x/Lget_save_loc.c \ + s390x/Lglobal.c \ + s390x/Linit.c \ + s390x/Linit_local.c \ + s390x/Linit_remote.c \ + s390x/Lis_signal_frame.c \ + s390x/Lregs.c \ + s390x/Lreg_states_iterate.c \ + s390x/Lresume.c \ + s390x/Lstep.c \ + s390x/setcontext.S +endif # ARCH_S390X # The list of files that go into libunwind-s390x: -libunwind_s390x_la_SOURCES_s390x = $(libunwind_la_SOURCES_s390x_common) \ - $(libunwind_la_SOURCES_generic) \ - s390x/Gapply_reg_state.c s390x/Greg_states_iterate.c \ - s390x/Gcreate_addr_space.c s390x/Gget_save_loc.c s390x/Gglobal.c \ - s390x/Ginit.c s390x/Ginit_local.c s390x/Ginit_remote.c \ - s390x/Gget_proc_info.c s390x/Gregs.c s390x/Gresume.c \ - s390x/Gis_signal_frame.c s390x/Gstep.c +libunwind_s390x_la_SOURCES = \ + $(libunwind_la_SOURCES_s390x_common) \ + $(libunwind_la_SOURCES_generic) \ + s390x/Gapply_reg_state.c \ + s390x/Gcreate_addr_space.c \ + s390x/Gget_proc_info.c \ + s390x/Gget_save_loc.c \ + s390x/Gglobal.c \ + s390x/Ginit.c \ + s390x/Ginit_local.c \ + s390x/Ginit_remote.c \ + s390x/Gis_signal_frame.c \ + s390x/Gregs.c \ + s390x/Greg_states_iterate.c \ + s390x/Gresume.c \ + s390x/Gstep.c +libunwind_s390x_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_s390x_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf64.la \ + $(libunwind_libadd) + +### target Sh: +# The list of files that go into libunwind and libunwind-sh: +noinst_HEADERS += \ + sh/init.h \ + sh/offsets.h \ + sh/unwind_i.h +libunwind_la_SOURCES_sh_common = \ + $(libunwind_la_SOURCES_common) \ + sh/is_fpreg.c \ + sh/regname.c + +if ARCH_SH +# The list of files that go into libunwind: +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_sh_common) \ + $(libunwind_la_SOURCES_local) \ + sh/Lapply_reg_state.c \ + sh/Lcreate_addr_space.c \ + sh/Lget_proc_info.c \ + sh/Lget_save_loc.c \ + sh/Lglobal.c \ + sh/Linit.c \ + sh/Linit_local.c \ + sh/Linit_remote.c \ + sh/Lis_signal_frame.c \ + sh/Lregs.c \ + sh/Lreg_states_iterate.c \ + sh/Lresume.c \ + sh/Lstep.c +libunwind_setjmp_la_SOURCES += sh/siglongjmp.S +endif # ARCH_SH + +libunwind_sh_la_SOURCES = \ + $(libunwind_la_SOURCES_sh_common) \ + $(libunwind_la_SOURCES_generic) \ + sh/Gapply_reg_state.c \ + sh/Gcreate_addr_space.c \ + sh/Gget_proc_info.c \ + sh/Gget_save_loc.c \ + sh/Gglobal.c \ + sh/Ginit.c \ + sh/Ginit_local.c \ + sh/Ginit_remote.c \ + sh/Gis_signal_frame.c \ + sh/Gregs.c \ + sh/Greg_states_iterate.c \ + sh/Gresume.c \ + sh/Gstep.c +libunwind_sh_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_sh_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf32.la \ + $(libunwind_libadd) + +### target x86: +# The list of files that go both into libunwind and libunwind-x86: +noinst_HEADERS += \ + x86/init.h \ + x86/offsets.h \ + x86/unwind_i.h +libunwind_la_SOURCES_x86_common = \ + $(libunwind_la_SOURCES_common) \ + x86/is_fpreg.c \ + x86/regname.c + +if ARCH_X86 +# The list of files that go into libunwind: +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_x86_common) \ + $(libunwind_la_SOURCES_x86_os_local) \ + $(libunwind_la_SOURCES_local) \ + x86/Lapply_reg_state.c \ + x86/Lcreate_addr_space.c \ + x86/Lget_proc_info.c \ + x86/Lget_save_loc.c \ + x86/Lglobal.c \ + x86/Linit.c \ + x86/Linit_local.c \ + x86/Linit_remote.c \ + x86/Lregs.c \ + x86/Lreg_states_iterate.c \ + x86/Lresume.c \ + x86/Lstep.c + +libunwind_setjmp_la_SOURCES += \ + x86/longjmp.S \ + x86/siglongjmp.S +endif # ARCH_X86 + +# The list of files that go into libunwind-x86: +libunwind_x86_la_SOURCES = \ + $(libunwind_la_SOURCES_x86_common) \ + $(libunwind_la_SOURCES_x86_os) \ + $(libunwind_la_SOURCES_generic) \ + x86/Gapply_reg_state.c \ + x86/Gcreate_addr_space.c \ + x86/Gget_proc_info.c \ + x86/Gget_save_loc.c \ + x86/Gglobal.c \ + x86/Ginit.c \ + x86/Ginit_local.c \ + x86/Ginit_remote.c \ + x86/Gregs.c \ + x86/Greg_states_iterate.c \ + x86/Gresume.c \ + x86/Gstep.c +libunwind_x86_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_x86_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf32.la \ + $(libunwind_libadd) + +### target x86_64: +# The list of files that go both into libunwind and libunwind-x86_64: +noinst_HEADERS += \ + x86_64/init.h \ + x86_64/ucontext_i.h \ + x86_64/unwind_i.h +libunwind_la_SOURCES_x86_64_common = \ + $(libunwind_la_SOURCES_common) \ + x86_64/is_fpreg.c \ + x86_64/regname.c + +if ARCH_X86_64 +# The list of files that go into libunwind: +libunwind_la_SOURCES = \ + $(libunwind_la_SOURCES_x86_64_common) \ + $(libunwind_la_SOURCES_x86_64_os_local)\ + $(libunwind_la_SOURCES_local) \ + x86_64/getcontext.S \ + x86_64/Lapply_reg_state.c \ + x86_64/Lcreate_addr_space.c \ + x86_64/Lget_proc_info.c \ + x86_64/Lget_save_loc.c \ + x86_64/Lglobal.c \ + x86_64/Linit.c \ + x86_64/Linit_local.c \ + x86_64/Linit_remote.c \ + x86_64/Lregs.c \ + x86_64/Lreg_states_iterate.c \ + x86_64/Lresume.c \ + x86_64/Lstash_frame.c \ + x86_64/Lstep.c \ + x86_64/Ltrace.c \ + x86_64/setcontext.S + +libunwind_setjmp_la_SOURCES += \ + x86_64/longjmp.S \ + x86_64/siglongjmp.S +endif # ARCH_X86_64 + +# The list of files that go into libunwind-x86_64: +libunwind_x86_64_la_SOURCES = \ + $(libunwind_la_SOURCES_x86_64_common) \ + $(libunwind_la_SOURCES_x86_64_os) \ + $(libunwind_la_SOURCES_generic) \ + x86_64/Gapply_reg_state.c \ + x86_64/Gcreate_addr_space.c \ + x86_64/Gget_proc_info.c \ + x86_64/Gget_save_loc.c \ + x86_64/Gglobal.c \ + x86_64/Ginit.c \ + x86_64/Ginit_local.c \ + x86_64/Ginit_remote.c \ + x86_64/Gregs.c \ + x86_64/Greg_states_iterate.c \ + x86_64/Gresume.c \ + x86_64/Gstash_frame.c \ + x86_64/Gstep.c \ + x86_64/Gtrace.c +libunwind_x86_64_la_LDFLAGS = \ + $(COMMON_SO_LDFLAGS) \ + -version-info $(SOVERSION) +libunwind_x86_64_la_LIBADD = \ + libunwind-dwarf-generic.la \ + libunwind-elf64.la \ + $(libunwind_libadd) if REMOTE_ONLY install-exec-hook: @@ -562,252 +1235,6 @@ install-exec-hook: fi endif -if OS_LINUX - libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_linux) - libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_linux_local) - libunwind_la_SOURCES_x86_os = x86/Gos-linux.c - libunwind_x86_la_SOURCES_os = x86/getcontext-linux.S - libunwind_la_SOURCES_x86_os_local = x86/Los-linux.c - libunwind_la_SOURCES_x86_64_os = x86_64/Gos-linux.c - libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-linux.c - libunwind_la_SOURCES_arm_os = arm/Gos-linux.c - libunwind_la_SOURCES_arm_os_local = arm/Los-linux.c - libunwind_coredump_la_SOURCES += coredump/_UCD_access_reg_linux.c - libunwind_coredump_la_SOURCES += coredump/_UCD_get_threadinfo_prstatus.c - libunwind_coredump_la_SOURCES += coredump/_UCD_get_mapinfo_linux.c -endif - -if OS_HPUX - libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_hpux) - libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_hpux_local) -endif - -if OS_FREEBSD - libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_freebsd) - libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_freebsd_local) - libunwind_la_SOURCES_x86_os = x86/Gos-freebsd.c - libunwind_x86_la_SOURCES_os = x86/getcontext-freebsd.S - libunwind_la_SOURCES_x86_os_local = x86/Los-freebsd.c - libunwind_la_SOURCES_x86_64_os = x86_64/Gos-freebsd.c - libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-freebsd.c - libunwind_la_SOURCES_arm_os = arm/Gos-freebsd.c - libunwind_la_SOURCES_arm_os_local = arm/Los-freebsd.c - libunwind_coredump_la_SOURCES += coredump/_UCD_access_reg_freebsd.c - libunwind_coredump_la_SOURCES += coredump/_UCD_get_threadinfo_prstatus.c - libunwind_coredump_la_SOURCES += coredump/_UCD_get_mapinfo_generic.c -endif - -if OS_SOLARIS - libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_solaris) - libunwind_la_SOURCES_x86_64_os = x86_64/Gos-solaris.c - libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-solaris.c -endif - -if OS_QNX - libunwind_la_SOURCES_os = $(libunwind_la_SOURCES_os_qnx) - libunwind_la_SOURCES_os_local = $(libunwind_la_SOURCES_os_qnx_local) - libunwind_la_SOURCES_arm_os = arm/Gos-other.c - libunwind_la_SOURCES_arm_os_local = arm/Los-other.c -endif - -if ARCH_AARCH64 - lib_LTLIBRARIES += libunwind-aarch64.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_aarch64) - libunwind_aarch64_la_SOURCES = $(libunwind_aarch64_la_SOURCES_aarch64) - libunwind_aarch64_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_aarch64_la_LIBADD = libunwind-dwarf-generic.la - libunwind_aarch64_la_LIBADD += libunwind-elf64.la -if !REMOTE_ONLY - libunwind_aarch64_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += aarch64/siglongjmp.S -else -if ARCH_ARM - lib_LTLIBRARIES += libunwind-arm.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_arm) - libunwind_arm_la_SOURCES = $(libunwind_arm_la_SOURCES_arm) - libunwind_arm_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_arm_la_LIBADD = libunwind-dwarf-generic.la - libunwind_arm_la_LIBADD += libunwind-elf32.la -if !REMOTE_ONLY - libunwind_arm_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += arm/siglongjmp.S -else -if ARCH_IA64 - BUILT_SOURCES = Gcursor_i.h Lcursor_i.h -mk_Gcursor_i.s: $(srcdir)/ia64/mk_Gcursor_i.c - $(COMPILE) -S "$(srcdir)/ia64/mk_Gcursor_i.c" -o mk_Gcursor_i.s -mk_Lcursor_i.s: $(srcdir)/ia64/mk_Lcursor_i.c - $(COMPILE) -S "$(srcdir)/ia64/mk_Lcursor_i.c" -o mk_Lcursor_i.s -Gcursor_i.h: mk_Gcursor_i.s - "$(srcdir)/ia64/mk_cursor_i" mk_Gcursor_i.s > Gcursor_i.h -Lcursor_i.h: mk_Lcursor_i.s - "$(srcdir)/ia64/mk_cursor_i" mk_Lcursor_i.s > Lcursor_i.h - - lib_LTLIBRARIES += libunwind-ia64.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_ia64) - libunwind_ia64_la_SOURCES = $(libunwind_ia64_la_SOURCES_ia64) - libunwind_ia64_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_ia64_la_LIBADD = libunwind-elf64.la -if !REMOTE_ONLY - libunwind_ia64_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += ia64/setjmp.S ia64/sigsetjmp.S \ - ia64/longjmp.S ia64/siglongjmp.S -else -if ARCH_HPPA - lib_LTLIBRARIES += libunwind-hppa.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_hppa) - libunwind_hppa_la_SOURCES = $(libunwind_hppa_la_SOURCES_hppa) - libunwind_hppa_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_hppa_la_LIBADD = libunwind-dwarf-generic.la - libunwind_hppa_la_LIBADD += libunwind-elf32.la -if !REMOTE_ONLY - libunwind_hppa_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += hppa/siglongjmp.S -else -if ARCH_MIPS - lib_LTLIBRARIES += libunwind-mips.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_mips) - libunwind_mips_la_SOURCES = $(libunwind_mips_la_SOURCES_mips) - libunwind_mips_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_mips_la_LIBADD = libunwind-dwarf-generic.la - libunwind_mips_la_LIBADD += libunwind-elfxx.la -if !REMOTE_ONLY - libunwind_mips_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += mips/siglongjmp.S -else -if ARCH_TILEGX - lib_LTLIBRARIES += libunwind-tilegx.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_tilegx) - libunwind_tilegx_la_SOURCES = $(libunwind_tilegx_la_SOURCES_tilegx) - libunwind_tilegx_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_tilegx_la_LIBADD = libunwind-dwarf-generic.la - libunwind_tilegx_la_LIBADD += libunwind-elfxx.la -if !REMOTE_ONLY - libunwind_tilegx_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += tilegx/siglongjmp.S -else -if ARCH_RISCV - lib_LTLIBRARIES += libunwind-riscv.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_riscv) - libunwind_riscv_la_SOURCES = $(libunwind_riscv_la_SOURCES_riscv) - libunwind_riscv_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_riscv_la_LIBADD = libunwind-dwarf-generic.la - libunwind_riscv_la_LIBADD += libunwind-elf64.la -if !REMOTE_ONLY - libunwind_riscv_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += riscv/siglongjmp.S -else -if ARCH_LOONGARCH64 - lib_LTLIBRARIES += libunwind-loongarch64.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_loongarch64) - libunwind_loongarch64_la_SOURCES = $(libunwind_loongarch64_la_SOURCES_loongarch64) - libunwind_loongarch64_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_loongarch64_la_LIBADD = libunwind-dwarf-generic.la - libunwind_loongarch64_la_LIBADD += libunwind-elf64.la -if !REMOTE_ONLY - libunwind_loongarch64_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += loongarch64/siglongjmp.S -else -if ARCH_X86 - lib_LTLIBRARIES += libunwind-x86.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_x86) $(libunwind_x86_la_SOURCES_os) - libunwind_x86_la_SOURCES = $(libunwind_x86_la_SOURCES_x86) - libunwind_x86_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_x86_la_LIBADD = libunwind-dwarf-generic.la - libunwind_x86_la_LIBADD += libunwind-elf32.la -if !REMOTE_ONLY - libunwind_x86_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += x86/longjmp.S x86/siglongjmp.S -else -if ARCH_X86_64 - lib_LTLIBRARIES += libunwind-x86_64.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_x86_64) - libunwind_x86_64_la_SOURCES = $(libunwind_x86_64_la_SOURCES_x86_64) - libunwind_x86_64_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_x86_64_la_LIBADD = libunwind-dwarf-generic.la - libunwind_x86_64_la_LIBADD += libunwind-elf64.la -if !REMOTE_ONLY - libunwind_x86_64_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += x86_64/longjmp.S x86_64/siglongjmp.S -else -if ARCH_PPC32 - lib_LTLIBRARIES += libunwind-ppc32.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_ppc32) - libunwind_ppc32_la_SOURCES = $(libunwind_ppc32_la_SOURCES_ppc32) - libunwind_ppc32_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_ppc32_la_LIBADD = libunwind-dwarf-generic.la - libunwind_ppc32_la_LIBADD += libunwind-elf32.la -if !REMOTE_ONLY - libunwind_ppc32_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += ppc/longjmp.S ppc/siglongjmp.S -else -if ARCH_PPC64 - lib_LTLIBRARIES += libunwind-ppc64.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_ppc64) - libunwind_ppc64_la_SOURCES = $(libunwind_ppc64_la_SOURCES_ppc64) - libunwind_ppc64_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_ppc64_la_LIBADD = libunwind-dwarf-generic.la - libunwind_ppc64_la_LIBADD += libunwind-elf64.la -if !REMOTE_ONLY - libunwind_ppc64_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += ppc/longjmp.S ppc/siglongjmp.S -else -if ARCH_SH - lib_LTLIBRARIES += libunwind-sh.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_sh) - libunwind_sh_la_SOURCES = $(libunwind_sh_la_SOURCES_sh) - libunwind_sh_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_sh_la_LIBADD = libunwind-dwarf-generic.la - libunwind_sh_la_LIBADD += libunwind-elf32.la -if !REMOTE_ONLY - libunwind_sh_la_LIBADD += libunwind.la -lc -endif - libunwind_setjmp_la_SOURCES += sh/siglongjmp.S -else -if ARCH_S390X - lib_LTLIBRARIES += libunwind-s390x.la - libunwind_la_SOURCES = $(libunwind_la_SOURCES_s390x) - libunwind_s390x_la_SOURCES = $(libunwind_s390x_la_SOURCES_s390x) - libunwind_s390x_la_LDFLAGS = $(COMMON_SO_LDFLAGS) -version-info $(SOVERSION) - libunwind_s390x_la_LIBADD = libunwind-dwarf-generic.la - libunwind_s390x_la_LIBADD += libunwind-elf64.la -if !REMOTE_ONLY - libunwind_s390x_la_LIBADD += libunwind.la -lc -endif - -endif # ARCH_S390X -endif # ARCH_SH -endif # ARCH_PPC64 -endif # ARCH_PPC32 -endif # ARCH_X86_64 -endif # ARCH_X86 -endif # ARCH_LOONGARCH64 -endif # ARCH_RISCV -endif # ARCH_TILEGX -endif # ARCH_MIPS -endif # ARCH_HPPA -endif # ARCH_IA64 -endif # ARCH_ARM -endif # ARCH_AARCH64 - -# libunwind-setjmp depends on libunwind-$(arch). Therefore must be added -# at the end. -if BUILD_SETJMP -lib_LTLIBRARIES += libunwind-setjmp.la -endif - # # Don't link with standard libraries, because those may mention # libunwind already. @@ -821,30 +1248,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/include/tdep-$(arch) -I. AM_CCASFLAGS = $(AM_CPPFLAGS) noinst_HEADERS += unwind/unwind-internal.h -EXTRA_DIST = $(libunwind_la_SOURCES_aarch64) \ - $(libunwind_la_SOURCES_arm) \ - $(libunwind_la_SOURCES_hppa) \ - $(libunwind_la_SOURCES_ia64) \ - $(libunwind_la_EXTRAS_ia64) \ - $(libunwind_la_SOURCES_mips) \ - $(libunwind_la_SOURCES_sh) \ - $(libunwind_la_SOURCES_x86) \ - $(libunwind_la_SOURCES_os_freebsd) \ - $(libunwind_la_SOURCES_os_linux) \ - $(libunwind_la_SOURCES_os_hpux) \ - $(libunwind_la_SOURCES_os_qnx) \ - $(libunwind_la_SOURCES_os_solaris) \ - $(libunwind_la_SOURCES_common) \ - $(libunwind_la_SOURCES_local) \ - $(libunwind_la_SOURCES_generic) \ - $(libunwind_aarch64_la_SOURCES_aarch64) \ - $(libunwind_arm_la_SOURCES_arm) \ - $(libunwind_hppa_la_SOURCES_hppa) \ - $(libunwind_ia64_la_SOURCES_ia64) \ - $(libunwind_mips_la_SOURCES_mips) \ - $(libunwind_sh_la_SOURCES_sh) \ - $(libunwind_x86_la_SOURCES_x86) \ - $(libunwind_x86_64_la_SOURCES_x86_64) +EXTRA_DIST = $(libunwind_la_EXTRAS_ia64) MAINTAINERCLEANFILES = Makefile.in diff --git a/src/native/external/libunwind/src/aarch64/Gglobal.c b/src/native/external/libunwind/src/aarch64/Gglobal.c index 23bececa992cf6..854b54914db8b7 100644 --- a/src/native/external/libunwind/src/aarch64/Gglobal.c +++ b/src/native/external/libunwind/src/aarch64/Gglobal.c @@ -48,8 +48,6 @@ tdep_init (void) dwarf_init (); #ifndef UNW_REMOTE_ONLY - tdep_init_mem_validate (); - aarch64_local_addr_space_init (); #endif atomic_store(&tdep_init_done, 1); /* signal that we're initialized... */ diff --git a/src/native/external/libunwind/src/aarch64/Ginit.c b/src/native/external/libunwind/src/aarch64/Ginit.c index 57f484674f201a..6986dbb61a134a 100644 --- a/src/native/external/libunwind/src/aarch64/Ginit.c +++ b/src/native/external/libunwind/src/aarch64/Ginit.c @@ -2,6 +2,7 @@ Copyright (C) 2008 CodeSourcery Copyright (C) 2012 Tommi Rantala Copyright (C) 2013 Linaro Limited + Copyright 2022 Blackberry Limited This file is part of libunwind. @@ -44,7 +45,7 @@ static struct unw_addr_space local_addr_space; unw_addr_space_t unw_local_addr_space = &local_addr_space; static inline void * -uc_addr (unw_tdep_context_t *uc, int reg) +uc_addr (unw_context_t *uc, int reg) { if (reg == UNW_AARCH64_VG) { @@ -65,10 +66,21 @@ uc_addr (unw_tdep_context_t *uc, int reg) else if (reg == UNW_AARCH64_PC) return &uc->uc_mcontext.mc_gpregs.gp_elr; else if (reg >= UNW_AARCH64_V0 && reg <= UNW_AARCH64_V31) - return &GET_FPCTX(uc)->uc_mcontext.mc_fpregs.fp_q[reg - UNW_AARCH64_V0]; + return &uc->uc_mcontext.mc_fpregs.fp_q[reg - UNW_AARCH64_V0]; else return NULL; -#else /* __FreeBSD__ */ +#elif defined(__QNX__) + if (reg >= UNW_AARCH64_X0 && reg <= UNW_AARCH64_X30) + return &uc->uc_mcontext.cpu.gpr[reg]; + else if (reg == UNW_AARCH64_SP) + return &AARCH64_GET_REGSP(&uc->uc_mcontext.cpu); + else if (reg == UNW_AARCH64_PC) + return &AARCH64_GET_REGIP(&uc->uc_mcontext.cpu); + else if (reg >= UNW_AARCH64_V0 && reg <= UNW_AARCH64_V31) + return &uc->uc_mcontext.fpu.reg[reg - UNW_AARCH64_V0]; + else + return NULL; +# else /* !__FreeBSD && ! __QNX__ */ if (reg >= UNW_AARCH64_X0 && reg <= UNW_AARCH64_X30) return &uc->uc_mcontext.regs[reg]; else if (reg == UNW_AARCH64_SP) @@ -85,7 +97,7 @@ uc_addr (unw_tdep_context_t *uc, int reg) # ifdef UNW_LOCAL_ONLY HIDDEN void * -tdep_uc_addr (unw_tdep_context_t *uc, int reg) +tdep_uc_addr (unw_context_t *uc, int reg) { return uc_addr (uc, reg); } @@ -113,234 +125,6 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, } -static int mem_validate_pipe[2] = {-1, -1}; - -#ifdef HAVE_PIPE2 -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); -} -#else -static inline void -set_pipe_flags (int fd) -{ - int fd_flags = fcntl (fd, F_GETFD, 0); - int status_flags = fcntl (fd, F_GETFL, 0); - - fd_flags |= FD_CLOEXEC; - fcntl (fd, F_SETFD, fd_flags); - - status_flags |= O_NONBLOCK; - fcntl (fd, F_SETFL, status_flags); -} - -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe (pipefd); - set_pipe_flags(pipefd[0]); - set_pipe_flags(pipefd[1]); -} -#endif - -static inline void -open_pipe (void) -{ - if (mem_validate_pipe[0] != -1) - close (mem_validate_pipe[0]); - if (mem_validate_pipe[1] != -1) - close (mem_validate_pipe[1]); - - do_pipe2 (mem_validate_pipe); -} - -ALWAYS_INLINE -static int -write_validate (void *addr) -{ - int ret = -1; - ssize_t bytes = 0; - - do - { - char buf; - bytes = read (mem_validate_pipe[0], &buf, 1); - } - while ( errno == EINTR ); - - int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK); - if (!valid_read) - { - // re-open closed pipe - open_pipe (); - } - - do - { - ret = write (mem_validate_pipe[1], addr, 1); - } - while ( errno == EINTR ); - - return ret; -} - -static int (*mem_validate_func) (void *addr, size_t len); -static int msync_validate (void *addr, size_t len) -{ - if (msync (addr, len, MS_ASYNC) != 0) - { - return -1; - } - - return write_validate (addr); -} - -#ifdef HAVE_MINCORE -static int mincore_validate (void *addr, size_t len) -{ - unsigned char mvec[2]; /* Unaligned access may cross page boundary */ - - /* mincore could fail with EAGAIN but we conservatively return -1 - instead of looping. */ - if (mincore (addr, len, (unsigned char *)mvec) != 0) - { - return -1; - } - - return write_validate (addr); -} -#endif - -/* Initialise memory validation method. On linux kernels <2.6.21, - mincore() returns incorrect value for MAP_PRIVATE mappings, - such as stacks. If mincore() was available at compile time, - check if we can actually use it. If not, use msync() instead. */ -HIDDEN void -tdep_init_mem_validate (void) -{ - open_pipe (); - -#ifdef HAVE_MINCORE - unsigned char present = 1; - size_t len = unw_page_size; - unw_word_t addr = uwn_page_start((unw_word_t)&present); - unsigned char mvec[1]; - int ret; - while ((ret = mincore((void *)addr, len, (unsigned char *)mvec)) == -1 && - errno == EAGAIN) - { - } - if (ret == 0) - { - Debug(1, "using mincore to validate memory\n"); - mem_validate_func = mincore_validate; - } - else -#endif - { - Debug(1, "using msync to validate memory\n"); - mem_validate_func = msync_validate; - } -} - -/* Cache of already validated addresses */ -#define NLGA 4 -#if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD -// thread-local variant -static _Thread_local unw_word_t last_good_addr[NLGA]; -static _Thread_local int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == last_good_addr[i]) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (last_good_addr[victim] == 0) { - last_good_addr[victim] = addr; - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; -} - -#else -// global, thread safe variant -static _Atomic unw_word_t last_good_addr[NLGA]; -static _Atomic int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == atomic_load(&last_good_addr[i])) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = atomic_load(&lga_victim); - unw_word_t zero = 0; - for (i = 0; i < NLGA; i++) { - if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) { - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - atomic_store(&last_good_addr[victim], addr); - victim = (victim + 1) % NLGA; - atomic_store(&lga_victim, victim); -} -#endif - -static int -validate_mem (unw_word_t addr) -{ - size_t len; - - len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - if (is_cached_valid_mem(addr)) - return 0; - - if (mem_validate_func ((void *) addr, len) == -1) - return -1; - - cache_valid_mem(addr); - - return 0; -} - static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) @@ -348,18 +132,18 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (unlikely (write)) { Debug (16, "mem[%lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { /* validate address */ const struct cursor *c = (const struct cursor *)arg; if (likely (c != NULL) && unlikely (c->validate) - && unlikely (validate_mem (addr))) { + && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) { Debug (16, "mem[%016lx] -> invalid\n", addr); return -1; } - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (16, "mem[%lx] -> %lx\n", addr, *val); } return 0; @@ -370,7 +154,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, void *arg) { unw_word_t *addr; - unw_tdep_context_t *uc = ((struct cursor *)arg)->uc; + unw_context_t *uc = ((struct cursor *)arg)->uc; if (unw_is_fpreg (reg)) goto badreg; @@ -380,12 +164,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %lx\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %lx\n", unw_regname (reg), *val); } return 0; @@ -399,7 +183,7 @@ static int access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, int write, void *arg) { - unw_tdep_context_t *uc = ((struct cursor *)arg)->uc; + unw_context_t *uc = ((struct cursor *)arg)->uc; unw_fpreg_t *addr; if (!unw_is_fpreg (reg)) @@ -441,10 +225,21 @@ static unw_word_t empty_ptrauth_mask(unw_addr_space_t addr_space_unused, void *a return 0; } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, char *buf, size_t buf_len, unw_word_t *offp, void *arg) +{ + return _Uelf64_get_elf_filename(as, getpid(), ip, buf, buf_len, offp); +} + HIDDEN void aarch64_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -454,6 +249,7 @@ aarch64_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = aarch64_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; local_addr_space.acc.ptrauth_insn_mask = empty_ptrauth_mask; local_addr_space.big_endian = target_is_big_endian(); unw_flush_cache (&local_addr_space, 0, 0); diff --git a/src/native/external/libunwind/src/aarch64/Ginit_local.c b/src/native/external/libunwind/src/aarch64/Ginit_local.c index 4a055fb0938ba5..5bd6c8ae81fe9a 100644 --- a/src/native/external/libunwind/src/aarch64/Ginit_local.c +++ b/src/native/external/libunwind/src/aarch64/Ginit_local.c @@ -61,7 +61,7 @@ unw_init_local (unw_cursor_t *cursor, unw_context_t *uc) } int -unw_init_local2 (unw_cursor_t *cursor, unw_tdep_context_t *uc, int flag) +unw_init_local2 (unw_cursor_t *cursor, unw_context_t *uc, int flag) { if (!flag) { diff --git a/src/native/external/libunwind/src/aarch64/Gis_signal_frame.c b/src/native/external/libunwind/src/aarch64/Gis_signal_frame.c index 67159d83961431..ace0e166145182 100644 --- a/src/native/external/libunwind/src/aarch64/Gis_signal_frame.c +++ b/src/native/external/libunwind/src/aarch64/Gis_signal_frame.c @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2012 Tommi Rantala Copyright (C) 2013 Linaro Limited + Copyright 2022-2023 Blackberry Limited. This file is part of libunwind. @@ -25,16 +26,41 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" -/* The restorer stub will always have the form: - - d2801168 movz x8, #0x8b - d4000001 svc #0x0 -*/ +#if defined(__linux__) +/* + * The restorer stub will always have the form: + * + * d2801168 movz x8, #0x8b + * d4000001 svc #0x0 + */ +# define SIGNAL_RETURN 0xd4000001d2801168 +#elif defined(__FreeBSD__) +/* + * The restorer stub will always have the form: + * + * 910003e0 mov x0, sp + * 91014000 add x0, x0, #SF_UC + * d2803428 mov x8, #SYS_sigreturn + * d4000001 svc 0 + */ +# define SIGNAL_RETURN 0x91014000910003e0 +#elif defined(__QNX__) +/* + * The restorer stub will always have the form: + * + * f9400260 ldr x0, [x19] + * d63f0060 blr x3 + * aa1303e0 mov x0, x19 + * 14xxxxxx b SignalReturn@plt + */ +# define SIGNAL_RETURN 0x14000000aa1303e0 +# define HANDLER_CALL 0xd63f0060f9400260 +#endif int unw_is_signal_frame (unw_cursor_t *cursor) { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__QNX__) struct cursor *c = (struct cursor *) cursor; unw_word_t w0, ip; unw_addr_space_t as; @@ -52,9 +78,29 @@ unw_is_signal_frame (unw_cursor_t *cursor) if (ret < 0) return ret; - /* FIXME: distinguish 32bit insn vs 64bit registers. */ - if (w0 != 0xd4000001d2801168) + if ((w0 & SIGNAL_RETURN) != SIGNAL_RETURN) + return 0; +#if defined(__FreeBSD__) + ip += 8; + /* + */ + ret = (*a->access_mem) (as, ip, &w0, 0, arg); + if (ret < 0) + return ret; + if (w0 != 0xd4000001d2803428) return 0; +#elif defined(__QNX__) + unw_word_t w1 = 0; + ret = (*a->access_mem) (as, ip-sizeof(w1), &w1, 0, arg); + if (ret < 0) + return ret; + + if ((w1 & HANDLER_CALL) != HANDLER_CALL) + { + return 0; + } + +#endif return 1; diff --git a/src/native/external/libunwind/src/tilegx/Ginit_local.c b/src/native/external/libunwind/src/aarch64/Gos-freebsd.c similarity index 50% rename from src/native/external/libunwind/src/tilegx/Ginit_local.c rename to src/native/external/libunwind/src/aarch64/Gos-freebsd.c index 029558675fd99b..f92fe8ee119de9 100644 --- a/src/native/external/libunwind/src/tilegx/Ginit_local.c +++ b/src/native/external/libunwind/src/aarch64/Gos-freebsd.c @@ -1,6 +1,5 @@ /* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. + Copyright (C) 2023 Dmitry Chagin This file is part of libunwind. @@ -23,58 +22,55 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "unwind_i.h" -#include "init.h" - -#ifdef UNW_REMOTE_ONLY - -int -unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) -{ - return -UNW_EINVAL; -} - -#else /* !UNW_REMOTE_ONLY */ - -static int -unw_init_local_common(unw_cursor_t *cursor, ucontext_t *uc, unsigned use_prev_instr) -{ - struct cursor *c = (struct cursor *) cursor; +#include - if (!atomic_load(&tdep_init_done)) - tdep_init (); +#include +#include +#include - memset(c, 0, sizeof(unw_cursor_t)); +#include - Debug (1, "(cursor=%p)\n", c); +#include "unwind_i.h" +#include "ucontext_i.h" - c->dwarf.as = unw_local_addr_space; +#ifndef UNW_REMOTE_ONLY - c->dwarf.as_arg = uc; - return common_init (c, use_prev_instr); -} +#define setcontext UNW_ARCH_OBJ(setcontext) +extern NORETURN void setcontext(ucontext_t *); -int -unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) +HIDDEN int +aarch64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) { - return unw_init_local_common(cursor, uc, 1); -} + struct cursor *c = (struct cursor *) cursor; -int -unw_init_local2 (unw_cursor_t *cursor, ucontext_t *uc, int flag) -{ - if (!flag) + /* + * XXX. Due to incorrectly handled cfi_signal_frame directive + * (it should mark current function, not a frame above) + * temporarily use unw_is_signal_frame to detect signal trampoline. + */ + if (unw_is_signal_frame (cursor)) { - return unw_init_local_common(cursor, uc, 1); - } - else if (flag == UNW_INIT_SIGNAL_FRAME) - { - return unw_init_local_common(cursor, uc, 0); + ucontext_t *uc = (ucontext_t *)(c->sigcontext_sp + offsetof(struct sigframe, sf_uc)); + + if (c->dwarf.eh_valid_mask & 0x1) + uc->uc_mcontext.mc_gpregs.gp_x[0] = c->dwarf.eh_args[0]; + if (c->dwarf.eh_valid_mask & 0x2) + uc->uc_mcontext.mc_gpregs.gp_x[1] = c->dwarf.eh_args[1]; + if (c->dwarf.eh_valid_mask & 0x4) + uc->uc_mcontext.mc_gpregs.gp_x[2] = c->dwarf.eh_args[2]; + + Debug (8, "resuming at ip=%llx via sigreturn(%p)\n", + (unsigned long long) c->sigcontext_pc, uc); + sigreturn(uc); + abort(); } else { - return -UNW_EINVAL; + setcontext(c->uc); } + + unreachable(); + return -UNW_EINVAL; } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/native/external/libunwind/src/aarch64/Gos-linux.c b/src/native/external/libunwind/src/aarch64/Gos-linux.c new file mode 100644 index 00000000000000..6614314dea3766 --- /dev/null +++ b/src/native/external/libunwind/src/aarch64/Gos-linux.c @@ -0,0 +1,145 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + Copyright (C) 2011-2013 Linaro Limited + Copyright (C) 2012 Tommi Rantala + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "unwind_i.h" + +#ifndef UNW_REMOTE_ONLY + +HIDDEN inline int +aarch64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +{ + struct cursor *c = (struct cursor *) cursor; + unw_tdep_context_t *uc = c->uc; + + if (c->sigcontext_format == AARCH64_SCF_NONE) + { + /* Since there are no signals involved here we restore EH and non scratch + registers only. */ + unsigned long regs[24]; + regs[0] = uc->uc_mcontext.regs[0]; + regs[1] = uc->uc_mcontext.regs[1]; + regs[2] = uc->uc_mcontext.regs[2]; + regs[3] = uc->uc_mcontext.regs[3]; + regs[4] = uc->uc_mcontext.regs[19]; + regs[5] = uc->uc_mcontext.regs[20]; + regs[6] = uc->uc_mcontext.regs[21]; + regs[7] = uc->uc_mcontext.regs[22]; + regs[8] = uc->uc_mcontext.regs[23]; + regs[9] = uc->uc_mcontext.regs[24]; + regs[10] = uc->uc_mcontext.regs[25]; + regs[11] = uc->uc_mcontext.regs[26]; + regs[12] = uc->uc_mcontext.regs[27]; + regs[13] = uc->uc_mcontext.regs[28]; + regs[14] = uc->uc_mcontext.regs[29]; /* FP */ + regs[15] = uc->uc_mcontext.regs[30]; /* LR */ + regs[16] = GET_FPCTX(uc)->vregs[8]; + regs[17] = GET_FPCTX(uc)->vregs[9]; + regs[18] = GET_FPCTX(uc)->vregs[10]; + regs[19] = GET_FPCTX(uc)->vregs[11]; + regs[20] = GET_FPCTX(uc)->vregs[12]; + regs[21] = GET_FPCTX(uc)->vregs[13]; + regs[22] = GET_FPCTX(uc)->vregs[14]; + regs[23] = GET_FPCTX(uc)->vregs[15]; + unsigned long sp = uc->uc_mcontext.sp; + + struct regs_overlay { + char x[sizeof(regs)]; + }; + + __asm__ __volatile__ ( + "mov x4, %0\n" + "mov x5, %1\n" + "ldp x0, x1, [x4]\n" + "ldp x2, x3, [x4,16]\n" + "ldp x19, x20, [x4,32]\n" + "ldp x21, x22, [x4,48]\n" + "ldp x23, x24, [x4,64]\n" + "ldp x25, x26, [x4,80]\n" + "ldp x27, x28, [x4,96]\n" + "ldp x29, x30, [x4,112]\n" + "ldp d8, d9, [x4,128]\n" + "ldp d10, d11, [x4,144]\n" + "ldp d12, d13, [x4,160]\n" + "ldp d14, d15, [x4,176]\n" + "mov sp, x5\n" + "ret \n" + : + : "r" (regs), + "r" (sp), + "m" (*(struct regs_overlay *)regs) + ); + } + else + { + struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; + + if (c->dwarf.eh_valid_mask & 0x1) sc->regs[0] = c->dwarf.eh_args[0]; + if (c->dwarf.eh_valid_mask & 0x2) sc->regs[1] = c->dwarf.eh_args[1]; + if (c->dwarf.eh_valid_mask & 0x4) sc->regs[2] = c->dwarf.eh_args[2]; + if (c->dwarf.eh_valid_mask & 0x8) sc->regs[3] = c->dwarf.eh_args[3]; + + sc->regs[4] = uc->uc_mcontext.regs[4]; + sc->regs[5] = uc->uc_mcontext.regs[5]; + sc->regs[6] = uc->uc_mcontext.regs[6]; + sc->regs[7] = uc->uc_mcontext.regs[7]; + sc->regs[8] = uc->uc_mcontext.regs[8]; + sc->regs[9] = uc->uc_mcontext.regs[9]; + sc->regs[10] = uc->uc_mcontext.regs[10]; + sc->regs[11] = uc->uc_mcontext.regs[11]; + sc->regs[12] = uc->uc_mcontext.regs[12]; + sc->regs[13] = uc->uc_mcontext.regs[13]; + sc->regs[14] = uc->uc_mcontext.regs[14]; + sc->regs[15] = uc->uc_mcontext.regs[15]; + sc->regs[16] = uc->uc_mcontext.regs[16]; + sc->regs[17] = uc->uc_mcontext.regs[17]; + sc->regs[18] = uc->uc_mcontext.regs[18]; + sc->regs[19] = uc->uc_mcontext.regs[19]; + sc->regs[20] = uc->uc_mcontext.regs[20]; + sc->regs[21] = uc->uc_mcontext.regs[21]; + sc->regs[22] = uc->uc_mcontext.regs[22]; + sc->regs[23] = uc->uc_mcontext.regs[23]; + sc->regs[24] = uc->uc_mcontext.regs[24]; + sc->regs[25] = uc->uc_mcontext.regs[25]; + sc->regs[26] = uc->uc_mcontext.regs[26]; + sc->regs[27] = uc->uc_mcontext.regs[27]; + sc->regs[28] = uc->uc_mcontext.regs[28]; + sc->regs[29] = uc->uc_mcontext.regs[29]; + sc->regs[30] = uc->uc_mcontext.regs[30]; + sc->sp = uc->uc_mcontext.sp; + sc->pc = uc->uc_mcontext.pc; + sc->pstate = uc->uc_mcontext.pstate; + + __asm__ __volatile__ ( + "mov sp, %0\n" + "ret %1\n" + : : "r" (c->sigcontext_sp), "r" (c->sigcontext_pc) + ); + } + unreachable(); + return -UNW_EINVAL; +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/native/external/libunwind/src/tilegx/is_fpreg.c b/src/native/external/libunwind/src/aarch64/Gos-qnx.c similarity index 83% rename from src/native/external/libunwind/src/tilegx/is_fpreg.c rename to src/native/external/libunwind/src/aarch64/Gos-qnx.c index d6d58969018882..756d346bed1cc5 100644 --- a/src/native/external/libunwind/src/tilegx/is_fpreg.c +++ b/src/native/external/libunwind/src/aarch64/Gos-qnx.c @@ -1,5 +1,4 @@ /* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery This file is part of libunwind. @@ -22,12 +21,15 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "libunwind_i.h" +#include "unwind_i.h" -/* TILEGX has no FP. */ +#ifndef UNW_REMOTE_ONLY -int -unw_is_fpreg (int regnum) +HIDDEN int +aarch64_local_resume (unw_addr_space_t as UNUSED, unw_cursor_t *cursor UNUSED, + void *arg UNUSED) { - return 0; + return -UNW_EINVAL; } + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/native/external/libunwind/src/aarch64/Gresume.c b/src/native/external/libunwind/src/aarch64/Gresume.c index 445bac70f0dd4a..bad2f60c7327a3 100644 --- a/src/native/external/libunwind/src/aarch64/Gresume.c +++ b/src/native/external/libunwind/src/aarch64/Gresume.c @@ -25,129 +25,6 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" -#include "offsets.h" - -#ifndef UNW_REMOTE_ONLY - -HIDDEN inline int -aarch64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) -{ -#ifdef __linux__ - struct cursor *c = (struct cursor *) cursor; - unw_tdep_context_t *uc = c->uc; - - if (c->sigcontext_format == AARCH64_SCF_NONE) - { - /* Since there are no signals involved here we restore EH and non scratch - registers only. */ - unsigned long regs[24]; - regs[0] = uc->uc_mcontext.regs[0]; - regs[1] = uc->uc_mcontext.regs[1]; - regs[2] = uc->uc_mcontext.regs[2]; - regs[3] = uc->uc_mcontext.regs[3]; - regs[4] = uc->uc_mcontext.regs[19]; - regs[5] = uc->uc_mcontext.regs[20]; - regs[6] = uc->uc_mcontext.regs[21]; - regs[7] = uc->uc_mcontext.regs[22]; - regs[8] = uc->uc_mcontext.regs[23]; - regs[9] = uc->uc_mcontext.regs[24]; - regs[10] = uc->uc_mcontext.regs[25]; - regs[11] = uc->uc_mcontext.regs[26]; - regs[12] = uc->uc_mcontext.regs[27]; - regs[13] = uc->uc_mcontext.regs[28]; - regs[14] = uc->uc_mcontext.regs[29]; /* FP */ - regs[15] = uc->uc_mcontext.regs[30]; /* LR */ - regs[16] = GET_FPCTX(uc)->vregs[8]; - regs[17] = GET_FPCTX(uc)->vregs[9]; - regs[18] = GET_FPCTX(uc)->vregs[10]; - regs[19] = GET_FPCTX(uc)->vregs[11]; - regs[20] = GET_FPCTX(uc)->vregs[12]; - regs[21] = GET_FPCTX(uc)->vregs[13]; - regs[22] = GET_FPCTX(uc)->vregs[14]; - regs[23] = GET_FPCTX(uc)->vregs[15]; - unsigned long sp = uc->uc_mcontext.sp; - - struct regs_overlay { - char x[sizeof(regs)]; - }; - - __asm__ __volatile__ ( - "mov x4, %0\n" - "mov x5, %1\n" - "ldp x0, x1, [x4]\n" - "ldp x2, x3, [x4,16]\n" - "ldp x19, x20, [x4,32]\n" - "ldp x21, x22, [x4,48]\n" - "ldp x23, x24, [x4,64]\n" - "ldp x25, x26, [x4,80]\n" - "ldp x27, x28, [x4,96]\n" - "ldp x29, x30, [x4,112]\n" - "ldp d8, d9, [x4,128]\n" - "ldp d10, d11, [x4,144]\n" - "ldp d12, d13, [x4,160]\n" - "ldp d14, d15, [x4,176]\n" - "mov sp, x5\n" - "ret \n" - : - : "r" (regs), - "r" (sp), - "m" (*(struct regs_overlay *)regs) - ); - } - else - { - struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; - - if (c->dwarf.eh_valid_mask & 0x1) sc->regs[0] = c->dwarf.eh_args[0]; - if (c->dwarf.eh_valid_mask & 0x2) sc->regs[1] = c->dwarf.eh_args[1]; - if (c->dwarf.eh_valid_mask & 0x4) sc->regs[2] = c->dwarf.eh_args[2]; - if (c->dwarf.eh_valid_mask & 0x8) sc->regs[3] = c->dwarf.eh_args[3]; - - sc->regs[4] = uc->uc_mcontext.regs[4]; - sc->regs[5] = uc->uc_mcontext.regs[5]; - sc->regs[6] = uc->uc_mcontext.regs[6]; - sc->regs[7] = uc->uc_mcontext.regs[7]; - sc->regs[8] = uc->uc_mcontext.regs[8]; - sc->regs[9] = uc->uc_mcontext.regs[9]; - sc->regs[10] = uc->uc_mcontext.regs[10]; - sc->regs[11] = uc->uc_mcontext.regs[11]; - sc->regs[12] = uc->uc_mcontext.regs[12]; - sc->regs[13] = uc->uc_mcontext.regs[13]; - sc->regs[14] = uc->uc_mcontext.regs[14]; - sc->regs[15] = uc->uc_mcontext.regs[15]; - sc->regs[16] = uc->uc_mcontext.regs[16]; - sc->regs[17] = uc->uc_mcontext.regs[17]; - sc->regs[18] = uc->uc_mcontext.regs[18]; - sc->regs[19] = uc->uc_mcontext.regs[19]; - sc->regs[20] = uc->uc_mcontext.regs[20]; - sc->regs[21] = uc->uc_mcontext.regs[21]; - sc->regs[22] = uc->uc_mcontext.regs[22]; - sc->regs[23] = uc->uc_mcontext.regs[23]; - sc->regs[24] = uc->uc_mcontext.regs[24]; - sc->regs[25] = uc->uc_mcontext.regs[25]; - sc->regs[26] = uc->uc_mcontext.regs[26]; - sc->regs[27] = uc->uc_mcontext.regs[27]; - sc->regs[28] = uc->uc_mcontext.regs[28]; - sc->regs[29] = uc->uc_mcontext.regs[29]; - sc->regs[30] = uc->uc_mcontext.regs[30]; - sc->sp = uc->uc_mcontext.sp; - sc->pc = uc->uc_mcontext.pc; - sc->pstate = uc->uc_mcontext.pstate; - - __asm__ __volatile__ ( - "mov sp, %0\n" - "ret %1\n" - : : "r" (c->sigcontext_sp), "r" (c->sigcontext_pc) - ); - } - unreachable(); -#else - printf ("%s: implement me\n", __FUNCTION__); -#endif - return -UNW_EINVAL; -} - -#endif /* !UNW_REMOTE_ONLY */ static inline void establish_machine_state (struct cursor *c) @@ -160,19 +37,17 @@ establish_machine_state (struct cursor *c) Debug (8, "copying out cursor state\n"); - for (reg = 0; reg <= UNW_AARCH64_V31; ++reg) + for (reg = 0; reg <= UNW_AARCH64_RA_SIGN_STATE; ++reg) + { + Debug (16, "copying %s %d\n", unw_regname (reg), reg); + if (tdep_access_reg (c, reg, &val, 0) >= 0) + as->acc.access_reg (as, reg, &val, 1, arg); + } + for (reg = UNW_AARCH64_V0; reg <= UNW_AARCH64_V31; ++reg) { Debug (16, "copying %s %d\n", unw_regname (reg), reg); - if (unw_is_fpreg (reg)) - { - if (tdep_access_fpreg (c, reg, &fpval, 0) >= 0) - as->acc.access_fpreg (as, reg, &fpval, 1, arg); - } - else - { - if (tdep_access_reg (c, reg, &val, 0) >= 0) - as->acc.access_reg (as, reg, &val, 1, arg); - } + if (tdep_access_fpreg (c, reg, &fpval, 0) >= 0) + as->acc.access_fpreg (as, reg, &fpval, 1, arg); } } @@ -191,8 +66,10 @@ unw_resume (unw_cursor_t *cursor) return -UNW_EINVAL; } +Debug(1, "==smw> before establish_machine_state\n"); establish_machine_state (c); +Debug(1, "==smw> before acc.resume\n"); return (*c->dwarf.as->acc.resume) (c->dwarf.as, (unw_cursor_t *) c, c->dwarf.as_arg); } diff --git a/src/native/external/libunwind/src/aarch64/Gstep.c b/src/native/external/libunwind/src/aarch64/Gstep.c index f4ef369d3a1096..3602da3d0530c8 100644 --- a/src/native/external/libunwind/src/aarch64/Gstep.c +++ b/src/native/external/libunwind/src/aarch64/Gstep.c @@ -2,6 +2,7 @@ Copyright (C) 2008 CodeSourcery Copyright (C) 2011-2013 Linaro Limited Copyright (C) 2012 Tommi Rantala + Copyright 2022 Blackberry Limited. This file is part of libunwind. @@ -25,38 +26,433 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "dwarf_i.h" +#include "ucontext_i.h" #include "unwind_i.h" -#include "offsets.h" + +static const int WSIZE = sizeof (unw_word_t); /* Recognise PLT entries such as: 40ddf0: b0000570 adrp x16, 4ba000 <_GLOBAL_OFFSET_TABLE_+0x2a8> 40ddf4: f9433611 ldr x17, [x16,#1640] 40ddf8: 9119a210 add x16, x16, #0x668 - 40ddfc: d61f0220 br x17 */ + 40ddfc: d61f0220 br x17 + + \note The current implementation only supports little endian modes. +*/ static int is_plt_entry (struct dwarf_cursor *c) { - unw_word_t w0, w1; + unw_word_t w0 = 0, w1 = 0; + unw_accessors_t *a; + + if (c->as->big_endian) + { + return 0; + } + + /* + A PLT (Procedure Linkage Table) is used by the dynamic linker to map the + relative address of a position independent function call onto the real + address of the function. If we attempt to unwind from any instruction + inside the PLT, and the PLT is missing DWARF unwind information, then + conventional unwinding will fail because although the function has been + "called" we have not yet entered the prologue and set-up the stack frame. + + This code looks to see if the instruction is anywhere within a "recognised" + PLT entry (note that the IP could be anywhere within the PLT, so we have to + examine nearby instructions). + */ + + struct instruction_entry + { + uint32_t pattern; + uint32_t mask; + } instructions[4] = + { + // aarch64 + {0x90000010,0x9f00001f}, // adrp + {0xf9400211,0xffc003ff}, // ldr + {0x91000210,0xff8003ff}, // add + {0xd61f0220,0xffffffff}, // br + }; + + a = unw_get_accessors (c->as); + if ((*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg) < 0) + { + return 0; + } + + /* + NB: the following code is endian sensitive! + + The current implementation is for little-endian modes, big-endian modes + will see the first instruction in the high bits of w0, and the second + instruction in the low bits of w0. Some tweaks will be needed to read from + the correct part of the word to support big endian modes. + */ + if ((w0 & instructions[0].mask) == instructions[0].pattern && + ((w0>>32) & instructions[1].mask) == instructions[1].pattern) + { + if ((*a->access_mem) (c->as, c->ip+8, &w1, 0, c->as_arg) >= 0 && + (w1 & instructions[2].mask) == instructions[2].pattern && + ((w1>>32) & instructions[3].mask) == instructions[3].pattern) + { + return 1; + } + else + { + return 0; + } + } + else if ((w0 & instructions[2].mask) == instructions[2].pattern && + ((w0>>32) & instructions[3].mask) == instructions[3].pattern) + { + w1 = w0; + if ((*a->access_mem) (c->as, c->ip-8, &w0, 0, c->as_arg) >= 0 && + (w0 & instructions[0].mask) == instructions[0].pattern && + ((w0>>32) & instructions[1].mask) == instructions[1].pattern) + { + return 1; + } + else + { + return 0; + } + } + else if ((w0 & instructions[1].mask) == instructions[1].pattern && + ((w0>>32) & instructions[2].mask) == instructions[2].pattern) + { + if ((*a->access_mem) (c->as, c->ip-4, &w0, 0, c->as_arg) < 0 || + (*a->access_mem) (c->as, c->ip+4, &w1, 0, c->as_arg) < 0) + { + return 0; + } + } + else if ((w0 & instructions[3].mask) == instructions[3].pattern) + { + if ((*a->access_mem) (c->as, c->ip-12, &w0, 0, c->as_arg) < 0 || + (*a->access_mem) (c->as, c->ip-4, &w1, 0, c->as_arg) < 0) + { + return 0; + } + } + + if ((w0 & instructions[0].mask) == instructions[0].pattern && + ((w0>>32) & instructions[1].mask) == instructions[1].pattern && + (w1 & instructions[2].mask) == instructions[2].pattern && + ((w1>>32) & instructions[3].mask) == instructions[3].pattern) + { + return 1; + } + else + { + return 0; + } +} + +typedef enum frame_record_location + { + NONE, /* frame record creation has not been detected, use LR */ + AT_SP_OFFSET, /* frame record creation has been detected, but FP + update not detected */ + AT_FP, /* frame record creation and FP update detected */ + } frame_record_location_t; + +typedef struct frame_state + { + frame_record_location_t loc; + int32_t offset; + } frame_state_t; + +/* Recognise when a frame record storing FP+LR has been created and whether FP + has been updated to point to the frame record. For example: + 4183d4: a9bd7bfd stp x29, x30, [sp,#-48]! <= FP+LR stored + 4183d8: d2800005 mov x5, #0x0 + 4183dc: d2800004 mov x4, #0x0 + 4183e0: 910003fd mov x29, sp <= FP updated + 4183e4: a90153f3 stp x19, x20, [sp,#16] + ... + 418444: a94153f3 ldp x19, x20, [sp,#16] + 418448: f94013f5 ldr x21, [sp,#32] + 41844c: a8c37bfd ldp x29, x30, [sp],#48 <= FP+LR retrieved + 418450: d65f03c0 ret +*/ +static frame_state_t +get_frame_state (unw_cursor_t *cursor) +{ + struct cursor *c = (struct cursor *) cursor; + unw_accessors_t *a = unw_get_accessors (c->dwarf.as); + unw_word_t w, start_ip, ip, offp; + frame_state_t fs; + fs.loc = NONE; + fs.offset = 0; + + /* PLT entries do not create frame records */ + if (is_plt_entry (&c->dwarf)) + return fs; + + /* Use get_proc_name to find start_ip of procedure */ + char name[128]; + if (((*a->get_proc_name) (c->dwarf.as, c->dwarf.ip, name, sizeof(name), &offp, c->dwarf.as_arg)) != 0) + return fs; + + start_ip = c->dwarf.ip - offp; + + /* Check for frame record instructions since the start of the procedure (start_ip). + * access_mem reads WSIZE bytes, so two instructions are checked in each iteration + */ + for (ip = start_ip; ip < c->dwarf.ip; ip += WSIZE) + { + if ((*a->access_mem) (c->dwarf.as, ip, &w, 0, c->dwarf.as_arg) < 0) + return fs; + + if (fs.loc == NONE) + { + /* Check that a 64-bit store pair (STP) instruction storing FP and LR + has been executed, which would indicate that a frame record has been + created and that the LR value is saved therein. + + From the ARM Architecture Reference Manual (ARMv8), the format of the + 64-bit STP instruction is + + | 10 | 101 | 0 | 0XX | 0 | imm7 | Rt2 | Rn | Rt | + + where X can be 0/1 above to indicate the instruction variant (post- + index, pre-index or signed offset). imm7 is a 7-bit signed immediate + offset. The following should be asserted for this check + + Rt2 == LR (x30) => 0b11110 + Rn == SP (x31) => 0b11111 + Rt == FP (x29) => 0b11101. + + Hence the bitmask should be constructed to assert that the instruction + is + + | 10 | 101 | 0 | 0XX | 0 | XXXXXXX | 11110 | 11111 | 11101 | + + So using a bitmask of 0xfe407fff, the masked instruction would be + 0xa8007bfd */ + if ((((w & 0xfe407fff00000000) == 0xa8007bfd00000000) && (c->dwarf.ip > ip + 4)) + || (((w & 0x00000000fe407fff) == 0x00000000a8007bfd) && (c->dwarf.ip > ip))) + { + fs.loc = AT_SP_OFFSET; + + /* If the signed offset variant of the STP instruction is detected, + the frame record may not be currently pointed to by SP. Extract + the offset from the STP instruction. + + From the ARM Architecture Reference Manual (ARMv8), the format of + the signed offset variant of the 64-bit STP instruction is + + | 10 | 101 | 0 | 010 | 0 | imm7 | Rt2 | Rn | Rt | + + Hence the bitmask should be constructed to assert that the + instruction is + + | 10 | 101 | 0 | 010 | 0 | XXXXXXX | 11110 | 11111 | 11101 | + + So using a bitmask of 0xffc07fff, the masked instruction would + be 0xa9007bdf. The offset value is (imm7 * 8) */ + if (((w & 0xffc07fff00000000) == 0xa9007bfd00000000) && (c->dwarf.ip > ip + 4)) + { + int32_t abs_offset = (w & 0x001f800000000000) >> 47; + fs.offset = ((w & 0x0020000000000000)? -abs_offset : abs_offset) * 8; + } + else if (((w & 0x00000000ffc07fff) == 0x00000000a9007bfd) && (c->dwarf.ip > ip)) + { + int32_t abs_offset = (w & 0x00000000001f8000) >> 15; + fs.offset = ((w & 0x0000000000200000)? -abs_offset : abs_offset) * 8; + } + else + fs.offset = 0; + + Debug (4, "ip=0x%lx => frame record stored at SP+0x%x\n", ip, fs.offset); + } + } + + if (fs.loc == AT_SP_OFFSET) + { + /* If the STP instruction has been executed, but not the instruction + to update FP, then the SP (with offset) should be used to find the + frame record, otherwise the FP should be used as there are no + guarantees that the SP is pointing to the frame record after the FP + has been updated. + + Two methods for updating the FP have been seen in practice. The + first is a MOV instruction. From the ARM Architecture Reference + Manual (ARMv8), the 64-bit MOV (to/from SP) instruction to update + FP (x29) is 0x910003fd. + + The second is an ADD instruction taking SP and FP as the source and + destination registers, respectively. From the ARM Architecture + Reference Manual (ARMv8), the 64-bit ADD (immediate) instruction is + + | 1 | 0 | 0 | 10001 | shift | imm12 | Rn | Rd | + + where the values of shift and imm12 are irrelevant for this check. + The following should be asserted + + Rn == SP (x31) => 0b11111 + Rd == FP (x29) => 0b11101. + + Hence the bitmask should be constructed to assert that the instruction + is + + | 1 | 0 | 0 | 10001 | XX | XXXXXXXXXXXX | 11111 | 11101 | + + So using a bitmask of 0xff0003ff, the masked instruction would be + 0x910003fd. + + Since the MOV instruction is an alias for ADD, it is sufficient to + only check for the latter case. */ + if ((((w & 0xff0003ff00000000) == 0x910003fd00000000) && (c->dwarf.ip > ip + 4)) + || (((w & 0x00000000ff0003ff) == 0x00000000910003fd) && (c->dwarf.ip > ip))) + { + fs.loc = AT_FP; + fs.offset = 0; + + Debug (4, "ip=0x%lx => frame record stored at FP\n", ip); + } + } + else if (fs.loc == AT_FP) + { + /* Check that a 64-bit load pair (LDP) instruction to restore FP and LR has been + executed, which would indicate that a branch or return instruction is about to + be executed and that FP is pointing to the caller's frame record instead. The + LR should be examined for the return address. In this case, the RA must have + already been authenticated. + + From the ARM Architecture Reference Manual (ARMv8), the format of the 64-bit + LDP instruction is + + | 10 | 101 | 0 | 0XX | 1 | imm7 | Rt2 | Rn | Rt | + + where X can be 0/1 above to indicate the instruction variant (post-index, pre- + index or signed offset). imm7 is a 7-bit signed immediate offset, which is + irrelevant for this check. However, the following should be asserted + + Rt2 == LR (x30) => 0b11110 + Rn == SP (x31) => 0b11111 + Rt == FP (x29) => 0b11101. + + Hence the bitmask should be constructed to assert that the instruction is + + | 10 | 101 | 0 | 0XX | 1 | XXXXXXX | 11110 | 11111 | 11101 | + + So using a bitmask of 0xfe407fff, the masked instruction would be 0xa8407bfd */ + if ((((w & 0xfe407fff00000000) == 0xa8407bfd00000000) && (c->dwarf.ip > ip + 4)) + || (((w & 0x00000000fe407fff) == 0x00000000a8407bfd) && (c->dwarf.ip > ip))) + { + fs.loc = NONE; + fs.offset = 0; + + Debug (4, "ip=0x%lx => frame record has been loaded, use LR\n", ip); + } + } + } + + Debug (3, "[start_ip = 0x%lx, ip=0x%lx) => loc = %d, offset = %d\n", + start_ip, c->dwarf.ip, fs.loc, fs.offset); + + return fs; +} + +#if defined __QNX__ +/* + * QNX kernel call have neither CFI nor save frame pointer, + * 00000000000549e8 : + * 549e8: cb0203e2 neg x2, x2 + * 549ec: 52800168 mov w8, #0xb // #11 + * 549f0: d4000a21 svc #0x51 + * 549f4: d65f03c0 ret + * 549f8: cb0003e0 neg x0, x0 + * 549fc: d65f03c0 ret + * + * From disassemble, QNX kernel call have mov followed by svc, and may with some + * neg instructions at beginning. + * 1. find procedure's ip start,end start + * 2. search mov,svc from begin, skip any neg instructions + */ +static bool is_neg_instr(uint32_t instr) +{ + /* 64bit register Xd */ + return ((instr & 0xffe003e0) == 0xcb0003e0); +} + +/* QNX use w8 to pass kernel call number */ +static bool is_mov_w8_instr(uint32_t instr) +{ + /* movz 32bit register Wd */ + return ((instr & 0xffe00008) == 0x52800008); +} + +static bool is_svc_instr(uint32_t instr) +{ + return instr == 0xd4000a21; +} + +static bool +is_qnx_kercall(struct dwarf_cursor *c) +{ + unw_word_t w0; unw_accessors_t *a; int ret; + unw_word_t proc_start_ip; + unw_word_t proc_end_ip; a = unw_get_accessors_int (c->as); - if ((ret = (*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg)) < 0 - || (ret = (*a->access_mem) (c->as, c->ip + 8, &w1, 0, c->as_arg)) < 0) - return 0; + if (c->as->big_endian || !a->get_proc_ip_range) + { + return false; + } + + ret = (*a->get_proc_ip_range) (c->as, c->ip, &proc_start_ip, &proc_end_ip, c->as_arg); + if (ret < 0) + { + Debug (2, "ip=0x%lx get proc ip range fail, ret = %d\n", c->ip, ret); + return false; + } + + unw_word_t ip = proc_start_ip; + while ((ip < proc_end_ip) && (ip + 8 < proc_end_ip)) + { + if ((*a->access_mem) (c->as, ip, &w0, 0, c->as_arg) < 0) + { + Debug (14, "access_mem ip=0x%lx fail\n", ip); + return false; + } - ret = (((w0 & 0xff0000009f000000) == 0xf900000090000000) - && ((w1 & 0xffffffffff000000) == 0xd61f022091000000)); + uint32_t low32 = w0 & 0xffffffff; + uint32_t high32 = w0 >> 32; + + if (is_mov_w8_instr(low32) && is_svc_instr(high32)) + { + return true; + } + if (is_neg_instr(low32) && is_neg_instr(high32)) + { + ip += 8; + } + else if (is_neg_instr(low32) && is_mov_w8_instr(high32)) + { + ip += 4; + } + else + { + return false; + } + } - Debug (14, "ip=0x%lx => 0x%016lx 0x%016lx, ret = %d\n", c->ip, w0, w1, ret); - return ret; + return false; } +#endif /* * Save the location of VL (vector length) from the signal frame to the VG (vector * granule) register if it exists, otherwise do nothing. If there is an error, * the location is also not modified. */ +#if defined __linux__ static int get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr) { @@ -103,6 +499,13 @@ get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr) } return 1; } +#else +static int +get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr) +{ + return 1; +} +#endif static int aarch64_handle_signal_frame (unw_cursor_t *cursor) @@ -117,20 +520,19 @@ aarch64_handle_signal_frame (unw_cursor_t *cursor) ret = unw_is_signal_frame (cursor); Debug(1, "unw_is_signal_frame()=%d\n", ret); - - /* Save the SP and PC to be able to return execution at this point - later in time (unw_resume). */ - c->sigcontext_sp = c->dwarf.cfa; - c->sigcontext_pc = c->dwarf.ip; - if (ret > 0) { - c->sigcontext_format = AARCH64_SCF_LINUX_RT_SIGFRAME; - sc_addr = sp_addr + sizeof (siginfo_t) + LINUX_UC_MCONTEXT_OFF; + c->sigcontext_format = SCF_FORMAT; + sc_addr = sp_addr + sizeof (siginfo_t) + UC_MCONTEXT_OFF; } else return -UNW_EUNSPEC; + /* Save the SP and PC to be able to return execution at this point + later in time (unw_resume). */ + c->sigcontext_sp = c->dwarf.cfa; + c->sigcontext_pc = c->dwarf.ip; + c->sigcontext_addr = sc_addr; c->frame_info.frame_type = UNW_AARCH64_FRAME_SIGRETURN; c->frame_info.cfa_reg_offset = sc_addr - sp_addr; @@ -141,40 +543,40 @@ aarch64_handle_signal_frame (unw_cursor_t *cursor) /* Update the dwarf cursor. Set the location of the registers to the corresponding addresses of the uc_mcontext / sigcontext structure contents. */ - c->dwarf.loc[UNW_AARCH64_X0] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X0_OFF); - c->dwarf.loc[UNW_AARCH64_X1] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X1_OFF); - c->dwarf.loc[UNW_AARCH64_X2] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X2_OFF); - c->dwarf.loc[UNW_AARCH64_X3] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X3_OFF); - c->dwarf.loc[UNW_AARCH64_X4] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X4_OFF); - c->dwarf.loc[UNW_AARCH64_X5] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X5_OFF); - c->dwarf.loc[UNW_AARCH64_X6] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X6_OFF); - c->dwarf.loc[UNW_AARCH64_X7] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X7_OFF); - c->dwarf.loc[UNW_AARCH64_X8] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X8_OFF); - c->dwarf.loc[UNW_AARCH64_X9] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X9_OFF); - c->dwarf.loc[UNW_AARCH64_X10] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X10_OFF); - c->dwarf.loc[UNW_AARCH64_X11] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X11_OFF); - c->dwarf.loc[UNW_AARCH64_X12] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X12_OFF); - c->dwarf.loc[UNW_AARCH64_X13] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X13_OFF); - c->dwarf.loc[UNW_AARCH64_X14] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X14_OFF); - c->dwarf.loc[UNW_AARCH64_X15] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X15_OFF); - c->dwarf.loc[UNW_AARCH64_X16] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X16_OFF); - c->dwarf.loc[UNW_AARCH64_X17] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X17_OFF); - c->dwarf.loc[UNW_AARCH64_X18] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X18_OFF); - c->dwarf.loc[UNW_AARCH64_X19] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X19_OFF); - c->dwarf.loc[UNW_AARCH64_X20] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X20_OFF); - c->dwarf.loc[UNW_AARCH64_X21] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X21_OFF); - c->dwarf.loc[UNW_AARCH64_X22] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X22_OFF); - c->dwarf.loc[UNW_AARCH64_X23] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X23_OFF); - c->dwarf.loc[UNW_AARCH64_X24] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X24_OFF); - c->dwarf.loc[UNW_AARCH64_X25] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X25_OFF); - c->dwarf.loc[UNW_AARCH64_X26] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X26_OFF); - c->dwarf.loc[UNW_AARCH64_X27] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X27_OFF); - c->dwarf.loc[UNW_AARCH64_X28] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X28_OFF); - c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X29_OFF); - c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_X30_OFF); - c->dwarf.loc[UNW_AARCH64_SP] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_SP_OFF); - c->dwarf.loc[UNW_AARCH64_PC] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_PC_OFF); - c->dwarf.loc[UNW_AARCH64_PSTATE] = DWARF_MEM_LOC (c->dwarf, sc_addr + LINUX_SC_PSTATE_OFF); + c->dwarf.loc[UNW_AARCH64_X0] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF); + c->dwarf.loc[UNW_AARCH64_X1] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 1); + c->dwarf.loc[UNW_AARCH64_X2] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 2); + c->dwarf.loc[UNW_AARCH64_X3] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 3); + c->dwarf.loc[UNW_AARCH64_X4] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 4); + c->dwarf.loc[UNW_AARCH64_X5] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 5); + c->dwarf.loc[UNW_AARCH64_X6] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 6); + c->dwarf.loc[UNW_AARCH64_X7] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 7); + c->dwarf.loc[UNW_AARCH64_X8] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 8); + c->dwarf.loc[UNW_AARCH64_X9] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 9); + c->dwarf.loc[UNW_AARCH64_X10] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 10); + c->dwarf.loc[UNW_AARCH64_X11] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 11); + c->dwarf.loc[UNW_AARCH64_X12] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 12); + c->dwarf.loc[UNW_AARCH64_X13] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 13); + c->dwarf.loc[UNW_AARCH64_X14] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 14); + c->dwarf.loc[UNW_AARCH64_X15] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 15); + c->dwarf.loc[UNW_AARCH64_X16] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 16); + c->dwarf.loc[UNW_AARCH64_X17] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 17); + c->dwarf.loc[UNW_AARCH64_X18] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 18); + c->dwarf.loc[UNW_AARCH64_X19] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 19); + c->dwarf.loc[UNW_AARCH64_X20] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 20); + c->dwarf.loc[UNW_AARCH64_X21] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 21); + c->dwarf.loc[UNW_AARCH64_X22] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 22); + c->dwarf.loc[UNW_AARCH64_X23] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 23); + c->dwarf.loc[UNW_AARCH64_X24] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 24); + c->dwarf.loc[UNW_AARCH64_X25] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 25); + c->dwarf.loc[UNW_AARCH64_X26] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 26); + c->dwarf.loc[UNW_AARCH64_X27] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 27); + c->dwarf.loc[UNW_AARCH64_X28] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 28); + c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_X29_OFF); + c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_X30_OFF); + c->dwarf.loc[UNW_AARCH64_SP] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_SP_OFF); + c->dwarf.loc[UNW_AARCH64_PC] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_PC_OFF); + c->dwarf.loc[UNW_AARCH64_PSTATE] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_PSTATE_OFF); c->dwarf.loc[UNW_AARCH64_VG] = DWARF_NULL_LOC; /* Set SP/CFA and PC/IP. */ @@ -192,6 +594,7 @@ unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; int validate = c->validate; + unw_word_t fp = 0; int ret; Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx))\n", @@ -213,29 +616,129 @@ unw_step (unw_cursor_t *cursor) dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X30], &c->dwarf.ip); } - /* Restore default memory validation state */ - c->validate = validate; - + /* Try DWARF-based unwinding... */ + c->sigcontext_format = AARCH64_SCF_NONE; ret = dwarf_step (&c->dwarf); Debug(1, "dwarf_step()=%d\n", ret); + /* Restore default memory validation state */ + c->validate = validate; + if (unlikely (ret == -UNW_ESTOPUNWIND)) return ret; + if (likely (ret > 0)) + { + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &fp); + if (ret == 0 && fp == 0) + { + /* Procedure Call Standard for the ARM 64-bit Architecture (AArch64) + * specifies that the end of the frame record chain is indicated by + * the address zero in the address for the previous frame. + */ + c->dwarf.ip = 0; + Debug (2, "NULL frame pointer X29 loc, returning 0\n"); + return 0; + } + } + if (unlikely (ret < 0)) { /* DWARF failed. */ + + /* + * We could get here because of missing/bad unwind information. + * Validate all addresses before dereferencing. + */ + if (c->dwarf.as == unw_local_addr_space) + { + c->validate = 1; + } + if (is_plt_entry (&c->dwarf)) { Debug (2, "found plt entry\n"); c->frame_info.frame_type = UNW_AARCH64_FRAME_STANDARD; } +#if defined __QNX__ + else if (is_qnx_kercall(&c->dwarf)) + { + Debug (2, "found qnx kernel call, fallback to use link register\n"); + c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED; + } +#endif else { - Debug (2, "fallback\n"); + /* Try use frame record. */ c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED; + } + + frame_state_t fs = get_frame_state(cursor); + + /* Prefer using frame record. The LR value is stored at an offset of + 8 into the frame record. */ + if (fs.loc != NONE) + { + if (fs.loc == AT_FP) + { + /* X29 points to frame record. */ + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &fp); + if (unlikely (ret == 0)) + { + if (fp == 0) + { + /* Procedure Call Standard for the ARM 64-bit Architecture (AArch64) + * specifies that the end of the frame record chain is indicated by + * the address zero in the address for the previous frame. + */ + c->dwarf.ip = 0; + Debug (2, "NULL frame pointer X29 loc, returning 0\n"); + return 0; + } + } + + for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; + + /* Frame record holds X29 and X30 values. */ + c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, fp); + c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, fp + 8); + } + else + { + /* Frame record stored but not pointed to by X29, use SP. */ + unw_word_t sp; + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_SP], &sp); + if (ret < 0) + return ret; + + for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; + + c->frame_info.cfa_reg_offset = fs.offset; + c->frame_info.cfa_reg_sp = 1; + + c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sp + fs.offset); + c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sp + fs.offset + 8); + } + + c->dwarf.loc[UNW_AARCH64_PC] = c->dwarf.loc[UNW_AARCH64_X30]; + + /* Set SP/CFA and PC/IP. */ + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &c->dwarf.cfa); + if (ret < 0) + return ret; + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip); + if (ret == 0) + { + ret = 1; + } + Debug (2, "fallback, CFA = 0x%016lx, IP = 0x%016lx returning %d\n", + c->dwarf.cfa, c->dwarf.ip, ret); + return ret; } - /* Use link register (X30). */ + + /* No frame record, fallback to link register (X30). */ c->frame_info.cfa_reg_offset = 0; c->frame_info.cfa_reg_sp = 0; c->frame_info.fp_cfa_offset = -1; diff --git a/src/native/external/libunwind/src/aarch64/Gtrace.c b/src/native/external/libunwind/src/aarch64/Gtrace.c index bcdf19296b31a1..555b4a50b45c45 100644 --- a/src/native/external/libunwind/src/aarch64/Gtrace.c +++ b/src/native/external/libunwind/src/aarch64/Gtrace.c @@ -25,7 +25,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" -#include "offsets.h" +#include "ucontext_i.h" #include #include @@ -70,7 +70,7 @@ trace_cache_free (void *arg) } tls_cache_destroyed = 1; tls_cache = NULL; - munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t)); + mi_munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t)); mempool_free (&trace_cache_pool, cache); Debug(5, "freed cache %p\n", cache); } @@ -152,7 +152,7 @@ trace_cache_expand (unw_trace_cache_t *cache) } Debug(5, "expanded cache from 2^%lu to 2^%lu buckets\n", cache->log_size, new_log_size); - munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t)); + mi_munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t)); cache->frames = new_frames; cache->log_size = new_log_size; cache->used = 0; @@ -407,7 +407,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) int depth = 0; int ret; - /* Check input parametres. */ + /* Check input parameters. */ if (unlikely(! cursor || ! buffer || ! size || (maxdepth = *size) <= 0)) return -UNW_EINVAL; @@ -483,13 +483,20 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) /* Advance standard traceable frame. */ cfa = (f->cfa_reg_sp ? sp : fp) + f->cfa_reg_offset; if (likely(f->lr_cfa_offset != -1)) - ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->lr_cfa_offset, pc); + { + ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->lr_cfa_offset, pc); + } else if (lr != 0) - { - /* Use the saved link register as the new pc. */ - pc = lr; - lr = 0; - } + { + /* Use the saved link register as the new pc. */ + pc = lr; + lr = 0; + } + else + { + /* Cached frame has no LR and neither do we. */ + return -UNW_ESTOPUNWIND; + } if (likely(ret >= 0) && likely(f->fp_cfa_offset != -1)) ACCESS_MEM_FAST(ret, c->validate, d, cfa + f->fp_cfa_offset, fp); @@ -503,15 +510,15 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) case UNW_AARCH64_FRAME_SIGRETURN: cfa = cfa + f->cfa_reg_offset; /* cfa now points to ucontext_t. */ - ACCESS_MEM_FAST(ret, c->validate, d, cfa + LINUX_SC_PC_OFF, pc); + ACCESS_MEM_FAST(ret, c->validate, d, cfa + SC_PC_OFF, pc); if (likely(ret >= 0)) - ACCESS_MEM_FAST(ret, c->validate, d, cfa + LINUX_SC_X29_OFF, fp); + ACCESS_MEM_FAST(ret, c->validate, d, cfa + SC_X29_OFF, fp); if (likely(ret >= 0)) - ACCESS_MEM_FAST(ret, c->validate, d, cfa + LINUX_SC_SP_OFF, sp); + ACCESS_MEM_FAST(ret, c->validate, d, cfa + SC_SP_OFF, sp); /* Save the link register here in case we end up in a function that doesn't save the link register in the prologue, e.g. kill. */ if (likely(ret >= 0)) - ACCESS_MEM_FAST(ret, c->validate, d, cfa + LINUX_SC_X30_OFF, lr); + ACCESS_MEM_FAST(ret, c->validate, d, cfa + SC_X30_OFF, lr); /* Resume stack at signal restoration point. The stack is not necessarily continuous here, especially with sigaltstack(). */ diff --git a/src/native/external/libunwind/src/tilegx/Linit_local.c b/src/native/external/libunwind/src/aarch64/Los-freebsd.c similarity index 81% rename from src/native/external/libunwind/src/tilegx/Linit_local.c rename to src/native/external/libunwind/src/aarch64/Los-freebsd.c index 68a1687e85444b..a75a205df19c01 100644 --- a/src/native/external/libunwind/src/tilegx/Linit_local.c +++ b/src/native/external/libunwind/src/aarch64/Los-freebsd.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Ginit_local.c" +#include "Gos-freebsd.c" #endif diff --git a/src/native/external/libunwind/src/tilegx/Lglobal.c b/src/native/external/libunwind/src/aarch64/Los-linux.c similarity index 82% rename from src/native/external/libunwind/src/tilegx/Lglobal.c rename to src/native/external/libunwind/src/aarch64/Los-linux.c index 6d7b489e14bd9f..3cc18aabcc399c 100644 --- a/src/native/external/libunwind/src/tilegx/Lglobal.c +++ b/src/native/external/libunwind/src/aarch64/Los-linux.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gglobal.c" +#include "Gos-linux.c" #endif diff --git a/src/native/external/libunwind/src/tilegx/Linit.c b/src/native/external/libunwind/src/aarch64/Los-qnx.c similarity index 83% rename from src/native/external/libunwind/src/tilegx/Linit.c rename to src/native/external/libunwind/src/aarch64/Los-qnx.c index e9abfdd46a3e0f..d5e4b82b9dd127 100644 --- a/src/native/external/libunwind/src/tilegx/Linit.c +++ b/src/native/external/libunwind/src/aarch64/Los-qnx.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Ginit.c" +#include "Gos-qnx.c" #endif diff --git a/src/native/external/libunwind/src/aarch64/gen-offsets.c b/src/native/external/libunwind/src/aarch64/gen-offsets.c deleted file mode 100644 index eadc2377d8f7a7..00000000000000 --- a/src/native/external/libunwind/src/aarch64/gen-offsets.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include -#include -#include - -#define UC(N,X) \ - printf ("#define LINUX_UC_" N "_OFF\t0x%X\n", offsetof (ucontext_t, X)) - -#define SC(N,X) \ - printf ("#define LINUX_SC_" N "_OFF\t0x%X\n", offsetof (struct sigcontext, X)) - -int -main (void) -{ - printf ( -"/* Linux-specific definitions: */\n\n" - -"/* Define various structure offsets to simplify cross-compilation. */\n\n" - -"/* Offsets for AArch64 Linux \"ucontext_t\": */\n\n"); - - UC ("FLAGS", uc_flags); - UC ("LINK", uc_link); - UC ("STACK", uc_stack); - UC ("MCONTEXT", uc_mcontext); - UC ("SIGMASK", uc_sigmask); - - printf ("\n/* Offsets for AArch64 Linux \"struct sigcontext\": */\n\n"); - - SC ("R0", regs[0]); - SC ("R1", regs[1]); - SC ("R2", regs[2]); - SC ("R3", regs[3]); - SC ("R4", regs[4]); - SC ("R5", regs[5]); - SC ("R6", regs[6]); - SC ("R7", regs[7]); - SC ("R8", regs[8]); - SC ("R9", regs[9]); - SC ("R10", regs[10]); - SC ("R11", regs[11]); - SC ("R12", regs[12]); - SC ("R13", regs[13]); - SC ("R14", regs[14]); - SC ("R15", regs[15]); - SC ("R16", regs[16]); - SC ("R17", regs[17]); - SC ("R18", regs[18]); - SC ("R19", regs[19]); - SC ("R20", regs[20]); - SC ("R21", regs[21]); - SC ("R22", regs[22]); - SC ("R23", regs[23]); - SC ("R24", regs[24]); - SC ("R25", regs[25]); - SC ("R26", regs[26]); - SC ("R27", regs[27]); - SC ("R28", regs[28]); - SC ("R29", regs[29]); - SC ("R30", regs[30]); - SC ("R31", regs[31]); - - SC ("PC", pc); - SC ("SP", sp); - SC ("Fault", fault_address); - SC ("state", pstate); - return 0; -} diff --git a/src/native/external/libunwind/src/aarch64/getcontext.S b/src/native/external/libunwind/src/aarch64/getcontext.S index 25ed5b66be73ea..6fcd17c488bc73 100644 --- a/src/native/external/libunwind/src/aarch64/getcontext.S +++ b/src/native/external/libunwind/src/aarch64/getcontext.S @@ -24,7 +24,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "offsets.h" +#include "ucontext_i.h" /* int _Uaarch64_getcontext_trace (unw_tdep_context_t *ucp) @@ -39,10 +39,10 @@ _Uaarch64_getcontext_trace: .cfi_startproc /* Save only FP, SP, PC - exclude this call. */ - str x29, [x0, #(LINUX_UC_MCONTEXT_OFF + LINUX_SC_X29_OFF)] + str x29, [x0, #(UC_MCONTEXT_OFF + SC_X29_OFF)] mov x9, sp - str x9, [x0, #(LINUX_UC_MCONTEXT_OFF + LINUX_SC_SP_OFF)] - str x30, [x0, #(LINUX_UC_MCONTEXT_OFF + LINUX_SC_PC_OFF)] + str x9, [x0, #(UC_MCONTEXT_OFF + SC_SP_OFF)] + str x30, [x0, #(UC_MCONTEXT_OFF + SC_PC_OFF)] ret .cfi_endproc diff --git a/src/native/external/libunwind/include/x86/jmpbuf.h b/src/native/external/libunwind/src/aarch64/longjmp.S similarity index 69% rename from src/native/external/libunwind/include/x86/jmpbuf.h rename to src/native/external/libunwind/src/aarch64/longjmp.S index 94d5984f08c496..a1b73f0509b6e7 100644 --- a/src/native/external/libunwind/include/x86/jmpbuf.h +++ b/src/native/external/libunwind/src/aarch64/longjmp.S @@ -1,6 +1,5 @@ /* libunwind - a platform-independent unwind library - Copyright (C) 2004 Hewlett-Packard Co - Contributed by David Mosberger-Tang + Copyright (C) 2023 Dmitry Chagin This file is part of libunwind. @@ -23,9 +22,25 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Use glibc's jump-buffer indices; NPTL peeks at SP: */ + .global _UI_longjmp_cont + .type _UI_longjmp_cont, @function +_UI_longjmp_cont: +#if defined(__linux__) || defined(__QNX__) + ret +#elif defined(__FreeBSD__) + .cfi_startproc + /* + * x0 - return address + * x1 - return value + */ + mov x30, x0 + mov x0, x1 + ret + .cfi_endproc +#else +#error Port me +#endif + .size _UI_longjmp_cont, . - _UI_longjmp_cont -#define JB_SP 4 -#define JB_RP 5 -#define JB_MASK_SAVED 6 -#define JB_MASK 7 + /* We do not need executable stack. */ + .section .note.GNU-stack,"",%progbits diff --git a/src/native/external/libunwind/src/aarch64/offsets.h b/src/native/external/libunwind/src/aarch64/offsets.h deleted file mode 100644 index 52291edb33d9cd..00000000000000 --- a/src/native/external/libunwind/src/aarch64/offsets.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Linux-specific definitions: */ - -/* Define various structure offsets to simplify cross-compilation. */ - -/* Offsets for AArch64 Linux "ucontext_t": */ - -#define LINUX_UC_FLAGS_OFF 0x0 -#define LINUX_UC_LINK_OFF 0x8 -#define LINUX_UC_STACK_OFF 0x10 -#define LINUX_UC_SIGMASK_OFF 0x28 -#define LINUX_UC_MCONTEXT_OFF 0xb0 - -/* Offsets for AArch64 Linux "struct sigcontext": */ - -#define LINUX_SC_FAULTADDRESS_OFF 0x00 -#define LINUX_SC_X0_OFF 0x008 -#define LINUX_SC_X1_OFF 0x010 -#define LINUX_SC_X2_OFF 0x018 -#define LINUX_SC_X3_OFF 0x020 -#define LINUX_SC_X4_OFF 0x028 -#define LINUX_SC_X5_OFF 0x030 -#define LINUX_SC_X6_OFF 0x038 -#define LINUX_SC_X7_OFF 0x040 -#define LINUX_SC_X8_OFF 0x048 -#define LINUX_SC_X9_OFF 0x050 -#define LINUX_SC_X10_OFF 0x058 -#define LINUX_SC_X11_OFF 0x060 -#define LINUX_SC_X12_OFF 0x068 -#define LINUX_SC_X13_OFF 0x070 -#define LINUX_SC_X14_OFF 0x078 -#define LINUX_SC_X15_OFF 0x080 -#define LINUX_SC_X16_OFF 0x088 -#define LINUX_SC_X17_OFF 0x090 -#define LINUX_SC_X18_OFF 0x098 -#define LINUX_SC_X19_OFF 0x0a0 -#define LINUX_SC_X20_OFF 0x0a8 -#define LINUX_SC_X21_OFF 0x0b0 -#define LINUX_SC_X22_OFF 0x0b8 -#define LINUX_SC_X23_OFF 0x0c0 -#define LINUX_SC_X24_OFF 0x0c8 -#define LINUX_SC_X25_OFF 0x0d0 -#define LINUX_SC_X26_OFF 0x0d8 -#define LINUX_SC_X27_OFF 0x0e0 -#define LINUX_SC_X28_OFF 0x0e8 -#define LINUX_SC_X29_OFF 0x0f0 -#define LINUX_SC_X30_OFF 0x0f8 -#define LINUX_SC_SP_OFF 0x100 -#define LINUX_SC_PC_OFF 0x108 -#define LINUX_SC_PSTATE_OFF 0x110 -#define LINUX_SC_RESERVED_OFF 0x120 - -// struct _aarch64_ctx { __u32 magic; __u32 size; }; -// struct sve_context { struct _aarch64_ctx head; __u16 vl; __u16 __reserved[3]; }; -#define LINUX_SC_RESERVED_MAGIC_OFF 0x0 -#define LINUX_SC_RESERVED_SIZE_OFF 0x4 -#define LINUX_SC_RESERVED_SVE_VL_OFF 0x8 \ No newline at end of file diff --git a/src/native/external/libunwind/src/aarch64/setcontext.S b/src/native/external/libunwind/src/aarch64/setcontext.S new file mode 100644 index 00000000000000..2ab1165f491888 --- /dev/null +++ b/src/native/external/libunwind/src/aarch64/setcontext.S @@ -0,0 +1,60 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2023 Dmitry Chagin + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "ucontext_i.h" + + + .global _Uaarch64_setcontext + .type _Uaarch64_setcontext, @function +_Uaarch64_setcontext: + .cfi_startproc + + /* + * Since there are no signals involved here we restore EH and + * non scratch registers only. + */ + + mov x9, x0 + ldp x0, x1, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF)] + ldp x2, x3, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 2 * 8)] + ldp x19, x20, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 19 * 8)] + ldp x21, x22, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 21 * 8)] + ldp x23, x24, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 23 * 8)] + ldp x25, x26, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 25 * 8)] + ldp x27, x28, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 27 * 8)] + ldp x29, x30, [x9, #(UC_MCONTEXT_OFF + SC_GPR_OFF + 29 * 8)] + + ldp q8, q9, [x9, #(UC_MCONTEXT_OFF + SC_FPSIMD_OFF + 8 * 16)] + ldp q10, q11, [x9, #(UC_MCONTEXT_OFF + SC_FPSIMD_OFF + 10 * 16)] + ldp q12, q13, [x9, #(UC_MCONTEXT_OFF + SC_FPSIMD_OFF + 12 * 16)] + ldp q14, q15, [x9, #(UC_MCONTEXT_OFF + SC_FPSIMD_OFF + 14 * 16)] + + ldr x9, [x9, #(UC_MCONTEXT_OFF + SC_SP_OFF)] + mov sp, x9 + + ret + .cfi_endproc + .size _Uaarch64_setcontext, . - _Uaarch64_setcontext + + .section .note.GNU-stack,"",@progbits diff --git a/src/native/external/libunwind/src/aarch64/siglongjmp.S b/src/native/external/libunwind/src/aarch64/siglongjmp.S index 9985c4b4aab378..92de7b2521a664 100644 --- a/src/native/external/libunwind/src/aarch64/siglongjmp.S +++ b/src/native/external/libunwind/src/aarch64/siglongjmp.S @@ -1,12 +1,59 @@ - /* Dummy implementation for now. */ +/* libunwind - a platform-independent unwind library + Copyright (C) 2023 Dmitry Chagin - .global _UI_siglongjmp_cont - .global _UI_longjmp_cont +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#if defined(__FreeBSD__) +#include +#define SIG_SETMASK 3 +#endif + + .global _UI_siglongjmp_cont + .type _UI_siglongjmp_cont, @function _UI_siglongjmp_cont: -_UI_longjmp_cont: +#if defined(__linux__) || defined(__QNX__) + ret +#elif defined(__FreeBSD__) + .cfi_startproc + /* + * x0 - return address + * x1 - return value + * x2 - mask + */ + stp x0, x1, [sp, #-16]! + .cfi_def_cfa_offset 16 + /* Restore the signal mask */ + mov x1, x2 /* set */ + mov x2, #0 /* oset */ + mov x0, #SIG_SETMASK + mov x8, #SYS_sigprocmask + svc 0 + ldp x30, x0, [sp], #16 + .cfi_def_cfa_offset 0 ret -#ifdef __linux__ - /* We do not need executable stack. */ - .section .note.GNU-stack,"",%progbits + .cfi_endproc +#else +#error Port me #endif + .size _UI_siglongjmp_cont, . - _UI_siglongjmp_cont + /* We do not need executable stack. */ + .section .note.GNU-stack,"",%progbits diff --git a/src/native/external/libunwind/src/aarch64/ucontext_i.h b/src/native/external/libunwind/src/aarch64/ucontext_i.h new file mode 100644 index 00000000000000..b1f99edba1b9a6 --- /dev/null +++ b/src/native/external/libunwind/src/aarch64/ucontext_i.h @@ -0,0 +1,74 @@ +/* Contributed by Dmitry Chagin . + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifndef libunwind_src_aarch64_ucontext_i_h +#define libunwind_src_aarch64_ucontext_i_h + +#if defined __FreeBSD__ || defined __APPLE__ + +#define UC_MCONTEXT_OFF 0x10 +#define SC_GPR_OFF 0x00 + +#define SC_X29_OFF 0x0e8 +#define SC_X30_OFF 0x0f0 +#define SC_SP_OFF 0x0f8 +#define SC_PC_OFF 0x100 +#define SC_PSTATE_OFF 0x108 +#define SC_FPSIMD_OFF 0x110 + +#define SCF_FORMAT AARCH64_SCF_FREEBSD_RT_SIGFRAME + +#elif defined(__linux__) + +#define UC_MCONTEXT_OFF 0xb0 +#define SC_GPR_OFF 0x08 + +#define SC_X29_OFF 0x0f0 +#define SC_X30_OFF 0x0f8 +#define SC_SP_OFF 0x100 +#define SC_PC_OFF 0x108 +#define SC_PSTATE_OFF 0x110 + +#define SCF_FORMAT AARCH64_SCF_LINUX_RT_SIGFRAME + +#define LINUX_SC_RESERVED_OFF 0x120 + +#define LINUX_SC_RESERVED_MAGIC_OFF 0x0 +#define LINUX_SC_RESERVED_SIZE_OFF 0x4 +#define LINUX_SC_RESERVED_SVE_VL_OFF 0x8 + +#elif defined(__QNX__) + +#define UC_MCONTEXT_OFF 48 +#define SC_GPR_OFF 0 +#define SC_X29_OFF 232 +#define SC_X30_OFF 240 +#define SC_SP_OFF 248 +#define SC_PC_OFF 256 +#define SC_PSTATE_OFF 264 +#define SCF_FORMAT AARCH64_SCF_QNX_RT_SIGFRAME + +#else +# error Port me +#endif + +#endif /* libunwind_src_aarch64_ucontext_i_h */ diff --git a/src/native/external/libunwind/src/aarch64/unwind_i.h b/src/native/external/libunwind/src/aarch64/unwind_i.h index 9305846f879e97..ab2e1d13fa2101 100644 --- a/src/native/external/libunwind/src/aarch64/unwind_i.h +++ b/src/native/external/libunwind/src/aarch64/unwind_i.h @@ -59,9 +59,7 @@ extern int aarch64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, } while (0) #endif -#if defined(__FreeBSD__) -#define GET_FPCTX(uc) ((unw_tdep_context_t *)(&uc->uc_mcontext.mc_spare)) -#else +#if defined(__linux__) #define GET_FPCTX(uc) ((unw_fpsimd_context_t *)(&uc->uc_mcontext.__reserved)) #endif diff --git a/src/native/external/libunwind/src/arm/Gex_tables.c b/src/native/external/libunwind/src/arm/Gex_tables.c index 40cd15346f32d5..56bbd0d07666c5 100644 --- a/src/native/external/libunwind/src/arm/Gex_tables.c +++ b/src/native/external/libunwind/src/arm/Gex_tables.c @@ -531,7 +531,7 @@ arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip, cb_data.di.format = -1; SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); - ret = dl_iterate_phdr (arm_phdr_cb, &cb_data); + ret = as->iterate_phdr_function (arm_phdr_cb, &cb_data); SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); if (cb_data.di.format != -1) diff --git a/src/native/external/libunwind/src/arm/Ginit.c b/src/native/external/libunwind/src/arm/Ginit.c index 680b2d47b54369..20071fd0130839 100644 --- a/src/native/external/libunwind/src/arm/Ginit.c +++ b/src/native/external/libunwind/src/arm/Ginit.c @@ -73,65 +73,28 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, return 0; } -/* Cache of already validated addresses */ -#define NLGA 4 -static unw_word_t last_good_addr[NLGA]; -static int lga_victim; - -static int -validate_mem (unw_word_t addr) -{ - int i, victim; - size_t len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - for (i = 0; i < NLGA; i++) - { - if (last_good_addr[i] && (addr == last_good_addr[i])) - return 0; - } - - if (msync ((void *) addr, len, MS_ASYNC) == -1) - return -1; - - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (!last_good_addr[victim]) { - last_good_addr[victim++] = addr; - return 0; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; - - return 0; -} static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { - /* validate address */ - const struct cursor *c = (const struct cursor *) arg; - if (c && validate_mem(addr)) - return -1; + const struct cursor *c = (const struct cursor *) arg; if (write) { Debug (16, "mem[%x] <- %x\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { - *val = *(unw_word_t *) addr; - Debug (16, "mem[%x] -> %x\n", addr, *val); + if (likely (c!= NULL) && unlikely (c->validate) + && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) + { + Debug (16, "mem[%#010lx] -> invalid\n", (long)addr); + return -1; + } + memcpy (val, (void *) addr, sizeof(unw_word_t)); + Debug (16, "mem[%#010lx] -> %#010lx\n", (long)addr, (long)*val); } return 0; } @@ -146,18 +109,17 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (unw_is_fpreg (reg)) goto badreg; -Debug (16, "reg = %s\n", unw_regname (reg)); if (!(addr = uc_addr (uc, reg))) goto badreg; if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %x\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %x\n", unw_regname (reg), *val); } return 0; @@ -208,10 +170,23 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf32_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void arm_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = arm_find_proc_info; local_addr_space.acc.put_unwind_info = arm_put_unwind_info; @@ -221,6 +196,7 @@ arm_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = arm_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/arm/Gstep.c b/src/native/external/libunwind/src/arm/Gstep.c index e4ada651bce817..fccbbd5ff36a01 100644 --- a/src/native/external/libunwind/src/arm/Gstep.c +++ b/src/native/external/libunwind/src/arm/Gstep.c @@ -94,6 +94,8 @@ unw_step (unw_cursor_t *cursor) struct cursor *c = (struct cursor *) cursor; int ret = -UNW_EUNSPEC; int has_stopunwind = 0; + int validate = c->validate; + c->validate = 1; Debug (1, "(cursor=%p)\n", c); @@ -107,9 +109,15 @@ unw_step (unw_cursor_t *cursor) ret = arm_exidx_step (c); Debug(1, "arm_exidx_step()=%d\n", ret); if (ret > 0) - return 1; + { + c->validate = validate; + return 1; + } if (ret == 0) - return ret; + { + c->validate = validate; + return ret; + } if (ret == -UNW_ESTOPUNWIND) has_stopunwind = 1; } @@ -125,16 +133,21 @@ unw_step (unw_cursor_t *cursor) Debug(1, "dwarf_step()=%d\n", ret); if (likely (ret > 0)) - return 1; + { + c->validate = validate; + return 1; + } if (ret < 0 && ret != -UNW_ENOINFO) { Debug (2, "returning %d\n", ret); + c->validate = validate; return ret; } } #endif /* CONFIG_DEBUG_FRAME */ + c->validate = validate; // Before trying the fallback, if any unwind info tell us to stop, do that. if (has_stopunwind) return -UNW_ESTOPUNWIND; diff --git a/src/native/external/libunwind/src/arm/Gtrace.c b/src/native/external/libunwind/src/arm/Gtrace.c index 51fc281de352e6..a73f6cf0a946b2 100644 --- a/src/native/external/libunwind/src/arm/Gtrace.c +++ b/src/native/external/libunwind/src/arm/Gtrace.c @@ -70,7 +70,7 @@ trace_cache_free (void *arg) } tls_cache_destroyed = 1; tls_cache = NULL; - munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t)); + mi_munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t)); mempool_free (&trace_cache_pool, cache); Debug(5, "freed cache %p\n", cache); } @@ -153,7 +153,7 @@ trace_cache_expand (unw_trace_cache_t *cache) Debug(5, "expanded cache from 2^%u to 2^%u buckets\n", cache->log_size, new_log_size); - munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t)); + mi_munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t)); cache->frames = new_frames; cache->log_size = new_log_size; cache->used = 0; @@ -408,7 +408,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) int depth = 0; int ret; - /* Check input parametres. */ + /* Check input parameters. */ if (unlikely(! cursor || ! buffer || ! size || (maxdepth = *size) <= 0)) return -UNW_EINVAL; @@ -514,7 +514,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) if (likely(ret >= 0)) ACCESS_MEM_FAST(ret, c->validate, d, cfa + LINUX_SC_LR_OFF, lr); #elif defined(__FreeBSD__) - printf("XXX\n"); + #error implement UNW_ARM_FRAME_SIGRETURN on FreeBSD #endif /* Resume stack at signal restoration point. The stack is not @@ -526,7 +526,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) break; case UNW_ARM_FRAME_SYSCALL: - printf("XXX1\n"); + Dprintf ("%s: implement me\n", __FUNCTION__); break; default: diff --git a/src/native/external/libunwind/src/coredump/_UCD_access_mem.c b/src/native/external/libunwind/src/coredump/_UCD_access_mem.c index cdfc6220283529..9328b9b9f98226 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_access_mem.c +++ b/src/native/external/libunwind/src/coredump/_UCD_access_mem.c @@ -26,7 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "ucd_file_table.h" int -_UCD_access_mem (unw_addr_space_t as, +_UCD_access_mem (unw_addr_space_t as UNUSED, unw_word_t addr, unw_word_t *val, int write, diff --git a/src/native/external/libunwind/src/coredump/_UCD_access_reg_linux.c b/src/native/external/libunwind/src/coredump/_UCD_access_reg_linux.c index bb6826168e8847..302f7bdfd20a61 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_access_reg_linux.c +++ b/src/native/external/libunwind/src/coredump/_UCD_access_reg_linux.c @@ -27,9 +27,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UCD_internal.h" int -_UCD_access_reg (unw_addr_space_t as, - unw_regnum_t regnum, unw_word_t *valp, - int write, void *arg) +_UCD_access_reg (unw_addr_space_t as UNUSED, + unw_regnum_t regnum, + unw_word_t *valp, + int write, + void *arg) { struct UCD_info *ui = arg; @@ -51,9 +53,6 @@ _UCD_access_reg (unw_addr_space_t as, #elif defined(UNW_TARGET_SH) if (regnum > UNW_SH_PR) goto badreg; -#elif defined(UNW_TARGET_TILEGX) - if (regnum > UNW_TILEGX_CFA) - goto badreg; #elif defined(UNW_TARGET_IA64) || defined(UNW_TARGET_HPPA) || defined(UNW_TARGET_PPC32) || defined(UNW_TARGET_PPC64) if (regnum >= ARRAY_SIZE(ui->prstatus->pr_reg)) goto badreg; diff --git a/src/native/external/libunwind/src/coredump/_UCD_access_reg_qnx.c b/src/native/external/libunwind/src/coredump/_UCD_access_reg_qnx.c new file mode 100644 index 00000000000000..1ceaf6a14e66b4 --- /dev/null +++ b/src/native/external/libunwind/src/coredump/_UCD_access_reg_qnx.c @@ -0,0 +1,158 @@ +/** + * Extract filemap info from a coredump (QNX) + */ +/* + This file is part of libunwind. + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "_UCD_internal.h" + + +/** + * Access scalar CPU register from core file + * @param[in] as Pointer to unwind address space structure. + * @param[in] regnum Arch-specific index of register to access. + * @param[out] valp Pointer to value to write to to be read to. + * @param[in] write Direction of operation (1 == write, 0 == read register). + * @param[in] arg Arg passed through from back end (pointer to UCD_info). + * + * @returns 0 on success, <0 on error. + * + * Reads a value from an architecture-specific, OS-specific structure retrieved + * from the core file under analysis. + * + * This is the QNX-specific implementation. + */ +int +_UCD_access_reg (unw_addr_space_t as, + unw_regnum_t regnum, + unw_word_t *valp, + int write, + void *arg) +{ + if (write) + { + Debug(0, "write is not supported\n"); + return -UNW_EINVAL; + } + + struct UCD_info *ui = arg; + +#if defined(UNW_TARGET_X86) + switch (regnum) { + case UNW_X86_EAX: + *valp = ui->prstatus->greg.x86.eax; + break; + case UNW_X86_EDX: + *valp = ui->prstatus->greg.x86.edx; + break; + case UNW_X86_ECX: + *valp = ui->prstatus->greg.x86.ecx; + break; + case UNW_X86_EBX: + *valp = ui->prstatus->greg.x86.ebx; + break; + case UNW_X86_ESI: + *valp = ui->prstatus->greg.x86.esi; + break; + case UNW_X86_EDI: + *valp = ui->prstatus->greg.x86.edi; + break; + case UNW_X86_EBP: + *valp = ui->prstatus->greg.x86.ebp; + break; + case UNW_X86_ESP: + *valp = ui->prstatus->greg.x86.esp; + break; + case UNW_X86_EIP: + *valp = ui->prstatus->greg.x86.eip; + break; + case UNW_X86_EFLAGS: + *valp = ui->prstatus->greg.x86.efl; + break; + default: + Debug(0, "bad regnum:%d\n", regnum); + return -UNW_EINVAL; + } +#elif defined(UNW_TARGET_X86_64) + switch (regnum) { + case UNW_X86_64_RAX: + *valp = ui->prstatus->greg.x86_64.rax; + break; + case UNW_X86_64_RDX: + *valp = ui->prstatus->greg.x86_64.rdx; + break; + case UNW_X86_64_RCX: + *valp = ui->prstatus->greg.x86_64.rcx; + break; + case UNW_X86_64_RBX: + *valp = ui->prstatus->greg.x86_64.rbx; + break; + case UNW_X86_64_RSI: + *valp = ui->prstatus->greg.x86_64.rsi; + break; + case UNW_X86_64_RDI: + *valp = ui->prstatus->greg.x86_64.rdi; + break; + case UNW_X86_64_RBP: + *valp = ui->prstatus->greg.x86_64.rbp; + break; + case UNW_X86_64_RSP: + *valp = ui->prstatus->greg.x86_64.rsp; + break; + case UNW_X86_64_RIP: + *valp = ui->prstatus->greg.x86_64.rip; + break; + default: + Debug(0, "bad regnum:%d\n", regnum); + return -UNW_EINVAL; + } +#elif defined(UNW_TARGET_ARM) + if (regnum >= UNW_ARM_R0 && regnum <= UNW_ARM_R16) { + *valp = ui->prstatus->greg.arm.gpr[regnum]; + } else { + Debug(0, "bad regnum:%d\n", regnum); + return -UNW_EINVAL; + } +#elif defined(UNW_TARGET_AARCH64) + if (regnum >= UNW_AARCH64_X0 && regnum <= UNW_AARCH64_X30) { + *valp = ui->prstatus->greg.aarch64.gpr[regnum]; + } else { + switch (regnum) { + case UNW_AARCH64_SP: + *valp = ui->prstatus->greg.aarch64.gpr[AARCH64_REG_SP]; + break; + case UNW_AARCH64_PC: + *valp = ui->prstatus->greg.aarch64.elr; + break; + default: + Debug(0, "bad regnum:%d\n", regnum); + return -UNW_EINVAL; + } + } + +#else +#error Port me +#endif + + return 0; +} + diff --git a/src/native/external/libunwind/src/coredump/_UCD_accessors.c b/src/native/external/libunwind/src/coredump/_UCD_accessors.c index ae5c23d21940b9..6d4a0b5c20564c 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_accessors.c +++ b/src/native/external/libunwind/src/coredump/_UCD_accessors.c @@ -32,5 +32,6 @@ unw_accessors_t _UCD_accessors = .access_reg = _UCD_access_reg, .access_fpreg = _UCD_access_fpreg, .resume = _UCD_resume, - .get_proc_name = _UCD_get_proc_name + .get_proc_name = _UCD_get_proc_name, + .get_elf_filename = _UCD_get_elf_filename }; diff --git a/src/native/external/libunwind/src/coredump/_UCD_create.c b/src/native/external/libunwind/src/coredump/_UCD_create.c index 784d0462513905..0faf8f1e9615bc 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_create.c +++ b/src/native/external/libunwind/src/coredump/_UCD_create.c @@ -54,6 +54,8 @@ _UCD_create(const char *filename) #define elf_header64 elf_header.h64 bool _64bits; + mi_init (); + struct UCD_info *ui = memset(malloc(sizeof(*ui)), 0, sizeof(*ui)); ui->edi.di_cache.format = -1; ui->edi.di_debug.format = -1; @@ -242,7 +244,7 @@ void _UCD_select_thread(struct UCD_info *ui, int n) pid_t _UCD_get_pid(struct UCD_info *ui) { #if defined(HAVE_PROCFS_STATUS) - return ui->prstatus->pid; + return ui->prstatus->thread.pid; #else return ui->prstatus->pr_pid; #endif @@ -251,7 +253,7 @@ pid_t _UCD_get_pid(struct UCD_info *ui) int _UCD_get_cursig(struct UCD_info *ui) { #if defined(HAVE_PROCFS_STATUS) - return 0; + return ui->prstatus->thread.info.si_signo; #else return ui->prstatus->pr_cursig; #endif diff --git a/src/native/external/libunwind/src/coredump/_UCD_elf_map_image.c b/src/native/external/libunwind/src/coredump/_UCD_elf_map_image.c index ea36745a6a026a..4f2ff7d1a7c68f 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_elf_map_image.c +++ b/src/native/external/libunwind/src/coredump/_UCD_elf_map_image.c @@ -43,10 +43,10 @@ CD_elf_map_image(struct UCD_info *ui, coredump_phdr_t *phdr) * these pages are allocated, but non-accessible. */ /* addr, length, prot, flags, fd, fd_offset */ - ei->image = mmap(NULL, phdr->p_memsz, PROT_READ, MAP_PRIVATE, ui->coredump_fd, phdr->p_offset); + ei->image = mi_mmap(NULL, phdr->p_memsz, PROT_READ, MAP_PRIVATE, ui->coredump_fd, phdr->p_offset); if (ei->image == MAP_FAILED) { - Debug(0, "error %d in mmap(): %s\n", errno, strerror(errno)); + Debug(0, "error in mmap()\n"); ei->image = NULL; return NULL; } @@ -55,7 +55,7 @@ CD_elf_map_image(struct UCD_info *ui, coredump_phdr_t *phdr) if (remainder_len > 0) { void *remainder_base = (char*) ei->image + phdr->p_filesz; - munmap(remainder_base, remainder_len); + mi_munmap(remainder_base, remainder_len); } } else { ucd_file_t *ucd_file = ucd_file_table_at(&ui->ucd_file_table, phdr->p_backing_file_index); @@ -76,7 +76,7 @@ CD_elf_map_image(struct UCD_info *ui, coredump_phdr_t *phdr) /* Check ELF header for sanity */ if (!elf_w(valid_object)(ei)) { - munmap(ei->image, ei->size); + mi_munmap(ei->image, ei->size); ei->image = NULL; ei->size = 0; return NULL; diff --git a/src/native/external/libunwind/src/coredump/_UCD_get_elf_filename.c b/src/native/external/libunwind/src/coredump/_UCD_get_elf_filename.c new file mode 100644 index 00000000000000..31146c55a4091c --- /dev/null +++ b/src/native/external/libunwind/src/coredump/_UCD_get_elf_filename.c @@ -0,0 +1,122 @@ +/* + This file is part of libunwind. + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "_UCD_lib.h" +#include "_UCD_internal.h" + +#if defined(HAVE_ELF_H) +# include +#elif defined(HAVE_SYS_ELF_H) +# include +#endif + +static off_t +_get_text_offset (uint8_t *image) +{ + off_t offset = 0; + typedef union + { + Elf32_Ehdr h32; + Elf64_Ehdr h64; + } elf_header_t; + + elf_header_t *elf_header = (elf_header_t *)image; + bool _64bits = (elf_header->h32.e_ident[EI_CLASS] == ELFCLASS64); + off_t e_phofs = _64bits ? elf_header->h64.e_phoff : elf_header->h32.e_phoff; + unsigned e_phnum = _64bits ? elf_header->h64.e_phnum : elf_header->h32.e_phnum; + + for (unsigned i = 0; i < e_phnum; ++i) + { + if (_64bits) + { + Elf64_Phdr *phdr = (Elf64_Phdr *) (image + e_phofs); + + if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X) == PF_X) + { + offset = phdr[i].p_offset; + break; + } + } + else + { + Elf32_Phdr *phdr = (Elf32_Phdr *) (image + e_phofs); + + if ((phdr[i].p_flags & PF_X) == PF_X) + { + offset = phdr[i].p_offset; + break; + } + } + } + + Debug (4, "returning offset %ld\n", (long)offset); + return offset; +} + +static int +elf_w (CD_get_elf_filename) (struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp) +{ + int ret = UNW_ESUCCESS; + + /* Used to be tdep_get_elf_image() in ptrace unwinding code */ + coredump_phdr_t *cphdr = _UCD_get_elf_image (ui, ip); + if (!cphdr) + { + Debug (1, "returns error: _UCD_get_elf_image failed\n"); + return -UNW_ENOINFO; + } + + const ucd_file_t *ucd_file = ucd_file_table_at(&ui->ucd_file_table, cphdr->p_backing_file_index); + if (!ucd_file) + { + Debug (1, "backing_fie_index:%d ucd_file_table_at failed\n", cphdr->p_backing_file_index); + return -UNW_ENOINFO; + } + + if (buf) + { + strncpy(buf, ucd_file->filename, buf_len); + buf[buf_len - 1] = '\0'; + if (strlen(ucd_file->filename) >= buf_len) + ret = -UNW_ENOMEM; + } + + /* Adjust IP to be relative to start of the .text section of the ELF file */ + if (offp) + *offp = ip - cphdr->p_vaddr + _get_text_offset (ui->edi.ei.image); + + return ret; +} + +int +_UCD_get_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, void *arg) +{ + struct UCD_info *ui = arg; +#if UNW_ELF_CLASS == UNW_ELFCLASS64 + return _Uelf64_CD_get_elf_filename (ui, as, ip, buf, buf_len, offp); +#elif UNW_ELF_CLASS == UNW_ELFCLASS32 + return _Uelf32_CD_get_elf_filename (ui, as, ip, buf, buf_len, offp); +#else + return -UNW_ENOINFO; +#endif +} diff --git a/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_linux.c b/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_linux.c index 0d04f01a86840a..1157ac14b9b495 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_linux.c +++ b/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_linux.c @@ -74,7 +74,7 @@ static const size_t mapinfo_offset = sizeof (core_nt_file_hdr_t); * various segments loaded into memory from ELF files with the ELF file from * which those segments were loaded. * - * This function links the file namess mapped in the CORE/NT_FILE note with + * This function links the file names mapped in the CORE/NT_FILE note with * the program headers in the core file through the UCD_info file table. * * Any file names that end in the string "(deleted)" are ignored. @@ -130,12 +130,12 @@ _handle_nt_file_note (uint8_t *desc, void *arg) * Note interpretation requires both name and type. */ static int -_handle_pt_note_segment (uint32_t n_namesz, - uint32_t n_descsz, - uint32_t n_type, - char *name, - uint8_t *desc, - void *arg) +_handle_pt_note_segment (uint32_t n_namesz UNUSED, + uint32_t n_descsz UNUSED, + uint32_t n_type, + char *name, + uint8_t *desc, + void *arg) { #ifdef NT_FILE if (n_type == NT_FILE && strcmp (name, "CORE") == 0) diff --git a/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_qnx.c b/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_qnx.c new file mode 100644 index 00000000000000..fce9ceced0d78c --- /dev/null +++ b/src/native/external/libunwind/src/coredump/_UCD_get_mapinfo_qnx.c @@ -0,0 +1,159 @@ +/** + * Extract filemap info from a coredump (QNX) + */ +/* + This file is part of libunwind. + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "_UCD_internal.h" + +#include + + +#define MAP_PATH_MAX 512 + + +/** Number of bytes for rounding up addresses. */ +static inline const size_t _roundup(size_t size) +{ +#if UNW_ELF_CLASS == UNW_ELFCLASS32 + static const size_t _roundup_sz = 4; +#else + static const size_t _roundup_sz = 8; +#endif + return (((size) + ((_roundup_sz)-1)) & ~((_roundup_sz)-1)); +} + + +/** + * Handle the QNX/QNT_LINK_MAP note type. + * @param[in] desc The note-specific data + * @param[in] arg The user-supplied callback argument + * + * The QNX/QNT_LINK_MAP note type contains a list of start/end virtual addresses + * within the core file and an associated filename. The purpose is to map + * various segments loaded into memory from ELF files with the ELF file from + * which those segments were loaded. + * + * This function links the file names mapped in the QNX/QNT_LINK_MAP note with + * the program headers in the core file through the UCD_info file table. + */ +static int +_handle_nt_file_note (uint8_t *desc, void *arg) +{ + struct UCD_info *ui = (struct UCD_info *)arg; + struct qnx_linkmap_note* linkmap_note = (struct qnx_linkmap_note*)desc; + uintptr_t data = (uintptr_t)&linkmap_note->data[0]; + + const size_t r_debug_size = _roundup(sizeof(struct qnx_r_debug)); + const struct qnx_link_map* linkmap = (struct qnx_link_map*)(data + r_debug_size); + const size_t link_count = (linkmap_note->header.linkmapsz - r_debug_size) / sizeof(struct qnx_link_map); + const char *const strtab = (const char *const)(data + _roundup(linkmap_note->header.linkmapsz)); + + for (size_t i = 0; i < link_count; ++i) + { + const struct qnx_link_map *map = &linkmap[i]; + for (size_t p = 0; p < ui->phdrs_count; ++p) + { + coredump_phdr_t *phdr = &ui->phdrs[p]; + if (phdr->p_type == PT_LOAD + && linkmap[i].l_addr >= phdr->p_vaddr + && linkmap[i].l_addr < phdr->p_vaddr + phdr->p_memsz) + { + char libpath[MAP_PATH_MAX]; + if ((0 == strncmp(strtab + linkmap[i].l_name, "PIE", 3)) + || (0 == strncmp(strtab + linkmap[i].l_name, "EXE", 3))) + { + snprintf (libpath, MAP_PATH_MAX, "%s", strtab + map->l_path); + } + else + { + snprintf (libpath, MAP_PATH_MAX, "%s%s", strtab + map->l_path, strtab + map->l_name); + } + phdr->p_backing_file_index = ucd_file_table_insert (&ui->ucd_file_table, libpath); + Debug(2, "added '%s' at index %d for phdr %zu\n", libpath, phdr->p_backing_file_index, p); + break; + } + } + } + + return UNW_ESUCCESS; +} + + +/** + * Callback to handle notes. + * @param[in] n_namesz size of name data + * @param[in] n_descsz size of desc data + * @param[in] n_type type of note + * @param[in] name zero-terminated string, n_namesz bytes plus alignment padding + * @param[in] desc note-specific data, n_descsz bytes plus alignment padding + * @param[in] arg user-supplied callback argument + */ +static int +_handle_pt_note_segment (uint32_t n_namesz UNUSED, + uint32_t n_descsz UNUSED, + uint32_t n_type, + char *name, + uint8_t *desc, + void *arg) +{ + if ((strcmp(name, QNX_NOTE_NAME) == 0) && n_type == QNT_LINK_MAP) + { + return _handle_nt_file_note (desc, arg); + } + return UNW_ESUCCESS; +} + + +/** + * Get filemap info from core file (QNX) + * @param[in] ui + * @param[in] phdrs + * @param[in] phdr_size + * + * If there is a mapinfo note in the core file, map its contents to the phdrs. + * + * Since there may or may not be any mapinfo notes it's OK for this function to + * fail. + */ +int +_UCD_get_mapinfo (struct UCD_info *ui, coredump_phdr_t *phdrs, unsigned phdr_size) +{ + int ret = UNW_ESUCCESS; /* it's OK if there are no file mappings */ + + for (unsigned i = 0; i < phdr_size; ++i) + { + if (phdrs[i].p_type == PT_NOTE) + { + uint8_t *segment; + size_t segment_size; + ret = _UCD_elf_read_segment (ui, &phdrs[i], &segment, &segment_size); + + if (ret == UNW_ESUCCESS) + { + _UCD_elf_visit_notes (segment, segment_size, _handle_pt_note_segment, ui); + free (segment); + } + } + } + + return ret; +} diff --git a/src/native/external/libunwind/src/coredump/_UCD_get_proc_name.c b/src/native/external/libunwind/src/coredump/_UCD_get_proc_name.c index cff587b0d1a331..f5d544e5620288 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_get_proc_name.c +++ b/src/native/external/libunwind/src/coredump/_UCD_get_proc_name.c @@ -84,8 +84,9 @@ static int elf_w (CD_get_proc_name) (struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip, char *buf, size_t buf_len, unw_word_t *offp) { - unsigned long segbase, mapoff; + unsigned long segbase; int ret; + /* We're about to map an elf image. The call will unmap memory it doesn't own, so just null it out and avoid that. */ @@ -104,6 +105,26 @@ elf_w (CD_get_proc_name) (struct UCD_info *ui, unw_addr_space_t as, unw_word_t i /* Adjust IP to be relative to start of the .text section of the ELF file */ ip = ip - cphdr->p_vaddr + _get_text_offset (ui->edi.ei.image); ret = elf_w (get_proc_name_in_image) (as, &ui->edi.ei, segbase, ip, buf, buf_len, offp); + if (ret == -UNW_ENOINFO) + { + /* maybe symtab is stripped, try debuglink */ + ucd_file_t *ucd_file = ucd_file_table_at(&ui->ucd_file_table, cphdr->p_backing_file_index); + if (ucd_file) + { + struct elf_image ei = {NULL, 0}; + ret = elf_w (load_debuginfo) (ucd_file->filename, &ei, 1); + if (ret == 0) + { + ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, ip, buf, buf_len, offp); + mi_munmap(ei.image, ei.size); + ei.image = NULL; + } + else + { + ret = -UNW_ENOINFO; + } + } + } return ret; } diff --git a/src/native/external/libunwind/src/coredump/_UCD_get_threadinfo_prstatus.c b/src/native/external/libunwind/src/coredump/_UCD_get_threadinfo_prstatus.c index 4644d1b1b6f8dc..c5927d9cf4026f 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_get_threadinfo_prstatus.c +++ b/src/native/external/libunwind/src/coredump/_UCD_get_threadinfo_prstatus.c @@ -1,19 +1,19 @@ /** - * Extract threadinfo from a coredump (targets with NT_PRSTATUS) + * Extract threadinfo from a coredump (supported targets) */ /* This file is part of libunwind. - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -35,13 +35,22 @@ * Accumulate a count of the number of thread notes * * This _UCD_elf_visit_notes() callback just increments a count for each - * NT_PRSTATUS note seen. + * thread status note seen. */ static int -_count_thread_notes(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char *name, uint8_t *desc, void *arg) +_count_thread_notes(uint32_t n_namesz UNUSED, + uint32_t n_descsz UNUSED, + uint32_t n_type, + char *name UNUSED, + uint8_t *desc UNUSED, + void *arg) { size_t *thread_count = (size_t *)arg; +#if defined(HAVE_PROCFS_STATUS) + if (0 == strcmp(name, QNX_NOTE_NAME) && n_type == QNT_CORE_STATUS) +#else if (n_type == NT_PRSTATUS) +#endif /* defined(HAVE_PROCFS_STATUS) */ { ++*thread_count; } @@ -52,25 +61,55 @@ _count_thread_notes(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char /** * Save a thread note to the unwind-coredump context * - * This _UCD_elf_visit_notes() callback just copies the actual data structure - * from any NT_PRSTATUS note seen into an array of such structures and - * increments the count. + * This _UCD_elf_visit_notes() callback just copies the actual data structure(s) + * from any notes seen into an array of such structures and increments the count. + * + * Some targets have multiple notes for each thread that MUST always come in the + * right order (eg. NT_PRSTATUS followed by NT_FPREGSET for Linux and the BSDs). + * The count only gets incremented on the first note for the thread so the + * remaining notes need to have their zero-based index adjusted. */ static int -_save_thread_notes(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char *name, uint8_t *desc, void *arg) +_save_thread_notes(uint32_t n_namesz UNUSED, + uint32_t n_descsz UNUSED, + uint32_t n_type, + char *name UNUSED, + uint8_t *desc, + void *arg) { struct UCD_info *ui = (struct UCD_info *)arg; +#if defined(HAVE_PROCFS_STATUS) + if (0 == strcmp(name, QNX_NOTE_NAME)) + { + switch (n_type) + { + case QNT_CORE_STATUS: + ++ui->n_threads; + memcpy(&ui->threads[ui->n_threads-1].prstatus.thread, desc, (size_t)n_descsz); + break; + case QNT_CORE_GREG: + memcpy(&ui->threads[ui->n_threads-1].prstatus.greg, desc, (size_t)n_descsz); + break; + case QNT_CORE_FPREG: + memcpy(&ui->threads[ui->n_threads-1].prstatus.fpreg, desc, (size_t)n_descsz); + break; + default: + break; + } + } +#else if (n_type == NT_PRSTATUS) - { - memcpy(&ui->threads[ui->n_threads].prstatus, desc, sizeof(UCD_proc_status_t)); - ++ui->n_threads; - } - if (n_type == NT_FPREGSET) - { + { + memcpy(&ui->threads[ui->n_threads].prstatus, desc, sizeof(UCD_proc_status_t)); + ++ui->n_threads; + } +#endif #ifdef HAVE_ELF_FPREGSET_T - memcpy(&ui->threads[ui->n_threads-1].fpregset, desc, sizeof(elf_fpregset_t)); + if (n_type == NT_FPREGSET) + { + memcpy(&ui->threads[ui->n_threads-1].fpregset, desc, sizeof(elf_fpregset_t)); + } #endif - } return UNW_ESUCCESS; } @@ -82,7 +121,7 @@ _save_thread_notes(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char * * and the process information is described by a note in the core file of type * NT_PRSTATUS. In fact, on Linux, the state of a thread is described by a * CPU-dependent group of notes but right now we're only going to care about the - * one process-status note. This statement is also true for FreeBSD. + * one process-status note. This statement is also true for the BSDs. * * Depending on how the core file is created, there may be one PT_NOTE segment * with multiple NT_PRSTATUS notes in it, or multiple PT_NOTE segments. Just to diff --git a/src/native/external/libunwind/src/coredump/_UCD_internal.h b/src/native/external/libunwind/src/coredump/_UCD_internal.h index 3a9434ab0244ba..e5dc89edf118df 100644 --- a/src/native/external/libunwind/src/coredump/_UCD_internal.h +++ b/src/native/external/libunwind/src/coredump/_UCD_internal.h @@ -80,7 +80,11 @@ typedef struct elf_prstatus UCD_proc_status_t; #elif defined(HAVE_STRUCT_PRSTATUS) typedef struct prstatus UCD_proc_status_t; #elif defined(HAVE_PROCFS_STATUS) -typedef procfs_status UCD_proc_status_t; +typedef struct { + procfs_status thread; + procfs_greg greg; + procfs_fpreg fpreg; +} UCD_proc_status_t; #else # error UCD_proc_status_t undefined #endif diff --git a/src/native/external/libunwind/src/coredump/_UPT_access_fpreg.c b/src/native/external/libunwind/src/coredump/_UPT_access_fpreg.c index a6d0bcea437694..be8dcffa13a0e3 100644 --- a/src/native/external/libunwind/src/coredump/_UPT_access_fpreg.c +++ b/src/native/external/libunwind/src/coredump/_UPT_access_fpreg.c @@ -25,8 +25,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UCD_internal.h" int -_UCD_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, - int write, void *arg) +_UCD_access_fpreg (unw_addr_space_t as UNUSED, + unw_regnum_t reg UNUSED, + unw_fpreg_t *val UNUSED, + int write, + void *arg) { struct UCD_info *ui UNUSED = arg; diff --git a/src/native/external/libunwind/src/coredump/_UPT_get_dyn_info_list_addr.c b/src/native/external/libunwind/src/coredump/_UPT_get_dyn_info_list_addr.c index 7548d63320f337..fd5c776983aaa8 100644 --- a/src/native/external/libunwind/src/coredump/_UPT_get_dyn_info_list_addr.c +++ b/src/native/external/libunwind/src/coredump/_UPT_get_dyn_info_list_addr.c @@ -79,8 +79,10 @@ get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg, DWARF2 unwind info. */ static inline int -get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg, - int *countp) +get_list_addr (unw_addr_space_t as UNUSED, + unw_word_t *dil_addr UNUSED, + void *arg UNUSED, + int *countp) { # warning Implement get_list_addr(), please. *countp = 0; diff --git a/src/native/external/libunwind/src/coredump/_UPT_put_unwind_info.c b/src/native/external/libunwind/src/coredump/_UPT_put_unwind_info.c index 462e1d048c3906..06d3bf44cf5268 100644 --- a/src/native/external/libunwind/src/coredump/_UPT_put_unwind_info.c +++ b/src/native/external/libunwind/src/coredump/_UPT_put_unwind_info.c @@ -27,7 +27,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UCD_internal.h" void -_UCD_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg) +_UCD_put_unwind_info (unw_addr_space_t as UNUSED, + unw_proc_info_t *pi, + void *arg UNUSED) { if (!pi->unwind_info) return; diff --git a/src/native/external/libunwind/src/coredump/_UPT_resume.c b/src/native/external/libunwind/src/coredump/_UPT_resume.c index a729c908cb123e..78cd6151eb2edb 100644 --- a/src/native/external/libunwind/src/coredump/_UPT_resume.c +++ b/src/native/external/libunwind/src/coredump/_UPT_resume.c @@ -27,7 +27,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UCD_internal.h" int -_UCD_resume (unw_addr_space_t as, unw_cursor_t *c, void *arg) +_UCD_resume (unw_addr_space_t as UNUSED, + unw_cursor_t *c UNUSED, + void *arg UNUSED) { print_error (__func__); print_error (" not implemented\n"); diff --git a/src/native/external/libunwind/src/coredump/ucd_file_table.c b/src/native/external/libunwind/src/coredump/ucd_file_table.c index cde693db58aa76..2a7294f219110d 100644 --- a/src/native/external/libunwind/src/coredump/ucd_file_table.c +++ b/src/native/external/libunwind/src/coredump/ucd_file_table.c @@ -93,6 +93,7 @@ _ucd_file_open (ucd_file_t *ucd_file) if (ucd_file->fd == -1) { Debug(0, "error %d in open(%s): %s\n", errno, ucd_file->filename, strerror(errno)); + return; } struct stat sbuf; @@ -115,7 +116,6 @@ ucd_file_map (ucd_file_t *ucd_file) { if (ucd_file->image != NULL) { - Debug(0, "warning, file \"%s\" already loaded\n", ucd_file->filename); return ucd_file->image; } @@ -124,10 +124,10 @@ ucd_file_map (ucd_file_t *ucd_file) _ucd_file_open (ucd_file); } - ucd_file->image = mmap(NULL, ucd_file->size, PROT_READ, MAP_PRIVATE, ucd_file->fd, 0); + ucd_file->image = mi_mmap(NULL, ucd_file->size, PROT_READ, MAP_PRIVATE, ucd_file->fd, 0); if (ucd_file->image == MAP_FAILED) { - Debug(0, "error %d in mmap(%s): %s\n", errno, ucd_file->filename, strerror(errno)); + Debug(0, "error in mmap(%s)\n", ucd_file->filename); ucd_file->image = NULL; return NULL; } diff --git a/src/native/external/libunwind/src/dl-iterate-phdr.c b/src/native/external/libunwind/src/dl-iterate-phdr.c index b14b765d9bf4e5..5c738a9ebc1c57 100644 --- a/src/native/external/libunwind/src/dl-iterate-phdr.c +++ b/src/native/external/libunwind/src/dl-iterate-phdr.c @@ -39,17 +39,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ (ehdr).e_ident[EI_MAG3] == ELFMAG3) #endif -typedef int (*unw_iterate_phdr_impl) (int (*callback) ( - struct dl_phdr_info *info, - size_t size, void *data), - void *data); - HIDDEN int -dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, size_t size, void *data), +dl_iterate_phdr (unw_iterate_phdr_callback_t callback, void *data) { static int initialized = 0; - static unw_iterate_phdr_impl libc_impl; + static unw_iterate_phdr_func_t libc_impl; int rc = 0; struct map_iterator mi; unsigned long start, end, offset, flags; diff --git a/src/native/external/libunwind/src/dwarf/Gexpr.c b/src/native/external/libunwind/src/dwarf/Gexpr.c index 882f4dac9642b2..4a8da2ce1bb3d2 100644 --- a/src/native/external/libunwind/src/dwarf/Gexpr.c +++ b/src/native/external/libunwind/src/dwarf/Gexpr.c @@ -110,7 +110,7 @@ static const uint8_t operands[256] = }; static inline unw_sword_t -sword (unw_addr_space_t as, unw_word_t val) +sword (unw_addr_space_t as UNUSED, unw_word_t val) { switch (dwarf_addr_size (as)) { diff --git a/src/native/external/libunwind/src/dwarf/Gfind_proc_info-lsb.c b/src/native/external/libunwind/src/dwarf/Gfind_proc_info-lsb.c index 8ead48f096fdd6..c11345e88383f2 100644 --- a/src/native/external/libunwind/src/dwarf/Gfind_proc_info-lsb.c +++ b/src/native/external/libunwind/src/dwarf/Gfind_proc_info-lsb.c @@ -120,7 +120,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, ei.image = NULL; *load_offset = 0; - ret = elf_w (load_debuglink) (file, &ei, is_local); + ret = elf_w (load_debuginfo) (file, &ei, is_local); if (ret != 0) return ret; @@ -128,7 +128,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, if (!shdr || (shdr->sh_offset + shdr->sh_size > ei.size)) { - munmap(ei.image, ei.size); + mi_munmap(ei.image, ei.size); return 1; } @@ -146,7 +146,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, if (!*buf) { Debug (2, "failed to allocate zlib .debug_frame buffer, skipping\n"); - munmap(ei.image, ei.size); + mi_munmap(ei.image, ei.size); return 1; } @@ -156,8 +156,8 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, if (ret != Z_OK) { Debug (2, "failed to decompress zlib .debug_frame, skipping\n"); - munmap(*buf, *bufsize); - munmap(ei.image, ei.size); + mi_munmap(*buf, *bufsize); + mi_munmap(ei.image, ei.size); return 1; } @@ -169,7 +169,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, { Debug (2, "unknown compression type %d, skipping\n", chdr->ch_type); - munmap(ei.image, ei.size); + mi_munmap(ei.image, ei.size); return 1; } } @@ -182,7 +182,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, if (!*buf) { Debug (2, "failed to allocate .debug_frame buffer, skipping\n"); - munmap(ei.image, ei.size); + mi_munmap(ei.image, ei.size); return 1; } @@ -207,7 +207,7 @@ load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local, break; } - munmap(ei.image, ei.size); + mi_munmap(ei.image, ei.size); return 0; } @@ -549,7 +549,7 @@ dwarf_find_eh_frame_section(struct dl_phdr_info *info) eh_frame); out: - munmap (ei.image, ei.size); + mi_munmap (ei.image, ei.size); return eh_frame; } @@ -804,7 +804,7 @@ dwarf_find_proc_info (unw_addr_space_t as, unw_word_t ip, cb_data.di_debug.format = -1; SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); - ret = dl_iterate_phdr (dwarf_callback, &cb_data); + ret = as->iterate_phdr_function (dwarf_callback, &cb_data); SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); if (ret > 0) @@ -966,7 +966,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, if (as == unw_local_addr_space) { e = lookup (table, table_len, ip - ip_base - di->load_offset); - if (e && &e[1] < &table[table_len / sizeof (unw_word_t)]) + if (e && &e[1] < &table[table_len / sizeof (struct table_entry)]) last_ip = e[1].start_ip_offset + ip_base + di->load_offset; else last_ip = di->end_ip; @@ -1037,7 +1037,7 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip, } HIDDEN void -dwarf_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg) +dwarf_put_unwind_info (unw_addr_space_t as UNUSED, unw_proc_info_t *pi UNUSED, void *arg UNUSED) { return; /* always a nop */ } diff --git a/src/native/external/libunwind/src/dwarf/Gfind_unwind_table.c b/src/native/external/libunwind/src/dwarf/Gfind_unwind_table.c index fb20fea0da449c..a7c4dfd3725887 100644 --- a/src/native/external/libunwind/src/dwarf/Gfind_unwind_table.c +++ b/src/native/external/libunwind/src/dwarf/Gfind_unwind_table.c @@ -36,12 +36,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define to_unw_word(p) ((unw_word_t) (uintptr_t) (p)) int -dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as, - const char *path, unw_word_t segbase, unw_word_t mapoff, - unw_word_t ip) +dwarf_find_unwind_table (struct elf_dyn_info *edi, + unw_addr_space_t as UNUSED, + const char *path UNUSED, + unw_word_t segbase, + unw_word_t mapoff, + unw_word_t ip UNUSED) { Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL; - unw_word_t addr, eh_frame_start, fde_count, load_base; + unw_word_t addr, eh_frame_start, fde_count, loadoff, load_base; unw_word_t max_load_addr = 0; unw_word_t start_ip = to_unw_word (-1); unw_word_t end_ip = 0; @@ -104,7 +107,8 @@ dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as, if (!ptxt) return 0; - load_base = segbase - ptxt->p_vaddr; + loadoff = mapoff + (ptxt->p_vaddr - ptxt->p_offset); + load_base = segbase - loadoff; start_ip += load_base; end_ip += load_base; diff --git a/src/native/external/libunwind/src/dwarf/Gget_proc_info_in_range.c b/src/native/external/libunwind/src/dwarf/Gget_proc_info_in_range.c index b9a369ba0626a2..5701c5d2d4ddf4 100644 --- a/src/native/external/libunwind/src/dwarf/Gget_proc_info_in_range.c +++ b/src/native/external/libunwind/src/dwarf/Gget_proc_info_in_range.c @@ -24,12 +24,17 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "dwarf_i.h" int -unw_get_proc_info_in_range (unw_word_t start_ip, unw_word_t end_ip, - unw_word_t eh_frame_table, unw_word_t eh_frame_table_len, - unw_word_t exidx_frame_table, unw_word_t exidx_frame_table_len, - unw_addr_space_t as, unw_word_t ip, - unw_proc_info_t *pi, int need_unwind_info, - void *arg) +unw_get_proc_info_in_range (unw_word_t start_ip, + unw_word_t end_ip, + unw_word_t eh_frame_table, + unw_word_t eh_frame_table_len UNUSED, + unw_word_t exidx_frame_table UNUSED, + unw_word_t exidx_frame_table_len UNUSED, + unw_addr_space_t as, + unw_word_t ip, + unw_proc_info_t *pi, + int need_unwind_info, + void *arg) { int ret = 0; @@ -53,11 +58,10 @@ unw_get_proc_info_in_range (unw_word_t start_ip, unw_word_t end_ip, if (eh_frame_table != 0) { unw_accessors_t *a = unw_get_accessors_int (as); - unw_word_t hdr; - if ((*a->access_mem)(as, eh_frame_table, &hdr, 0, arg) < 0) { + struct dwarf_eh_frame_hdr* exhdr = NULL; + if ((*a->access_mem)(as, eh_frame_table, (unw_word_t*)&exhdr, 0, arg) < 0) { return -UNW_EINVAL; } - struct dwarf_eh_frame_hdr* exhdr = (struct dwarf_eh_frame_hdr*)&hdr; if (exhdr->version != DW_EH_VERSION) { Debug (1, "Unexpected version %d\n", exhdr->version); diff --git a/src/native/external/libunwind/src/dwarf/Gparser.c b/src/native/external/libunwind/src/dwarf/Gparser.c index b7f83ca2dad597..7a5d7e1f0ff344 100644 --- a/src/native/external/libunwind/src/dwarf/Gparser.c +++ b/src/native/external/libunwind/src/dwarf/Gparser.c @@ -490,7 +490,9 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip) } static int -parse_dynamic (struct dwarf_cursor *c, unw_word_t ip, dwarf_state_record_t *sr) +parse_dynamic (struct dwarf_cursor *c UNUSED, + unw_word_t ip UNUSED, + dwarf_state_record_t *sr UNUSED) { Debug (1, "Not yet implemented\n"); return -UNW_ENOINFO; @@ -520,8 +522,10 @@ setup_fde (struct dwarf_cursor *c, dwarf_state_record_t *sr) for (i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i) set_reg (sr, i, DWARF_WHERE_SAME, 0); +#if !defined(UNW_TARGET_ARM) && !(defined(UNW_TARGET_MIPS) && _MIPS_SIM == _ABI64) // SP defaults to CFA (but is overridable) set_reg (sr, TDEP_DWARF_SP, DWARF_WHERE_CFA, 0); +#endif struct dwarf_cie_info *dci = c->pi.unwind_info; sr->rs_current.ret_addr_column = dci->ret_addr_column; @@ -571,14 +575,14 @@ dwarf_flush_rs_cache (struct dwarf_rs_cache *cache) cache->log_size = DWARF_DEFAULT_LOG_UNW_CACHE_SIZE; } else { if (cache->hash && cache->hash != cache->default_hash) - munmap(cache->hash, DWARF_UNW_HASH_SIZE(cache->prev_log_size) - * sizeof (cache->hash[0])); + mi_munmap(cache->hash, DWARF_UNW_HASH_SIZE(cache->prev_log_size) + * sizeof (cache->hash[0])); if (cache->buckets && cache->buckets != cache->default_buckets) - munmap(cache->buckets, DWARF_UNW_CACHE_SIZE(cache->prev_log_size) - * sizeof (cache->buckets[0])); + mi_munmap(cache->buckets, DWARF_UNW_CACHE_SIZE(cache->prev_log_size) + * sizeof (cache->buckets[0])); if (cache->links && cache->links != cache->default_links) - munmap(cache->links, DWARF_UNW_CACHE_SIZE(cache->prev_log_size) - * sizeof (cache->links[0])); + mi_munmap(cache->links, DWARF_UNW_CACHE_SIZE(cache->prev_log_size) + * sizeof (cache->links[0])); GET_MEMORY(cache->hash, DWARF_UNW_HASH_SIZE(cache->log_size) * sizeof (cache->hash[0])); GET_MEMORY(cache->buckets, DWARF_UNW_CACHE_SIZE(cache->log_size) @@ -961,7 +965,6 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) if (DWARF_IS_NULL_LOC (c->loc[rs->ret_addr_column])) { c->ip = 0; - ret = 0; } else { @@ -982,8 +985,8 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs) } #endif c->ip = ip; - ret = 1; } + ret = (c->ip != 0) ? 1 : 0; /* XXX: check for ip to be code_aligned */ if (c->ip == prev_ip && c->cfa == prev_cfa) @@ -1095,9 +1098,9 @@ dwarf_make_proc_info (struct dwarf_cursor *c) } static int -dwarf_reg_states_dynamic_iterate(struct dwarf_cursor *c, - unw_reg_states_callback cb, - void *token) +dwarf_reg_states_dynamic_iterate(struct dwarf_cursor *c UNUSED, + unw_reg_states_callback cb UNUSED, + void *token UNUSED) { Debug (1, "Not yet implemented\n"); return -UNW_ENOINFO; diff --git a/src/native/external/libunwind/src/elfxx.c b/src/native/external/libunwind/src/elfxx.c index 92bcde32cdb31d..e7b2d561f49f98 100644 --- a/src/native/external/libunwind/src/elfxx.c +++ b/src/native/external/libunwind/src/elfxx.c @@ -30,12 +30,40 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#ifdef HAVE_LZMA +#if HAVE_LZMA #include #endif /* HAVE_LZMA */ +struct symbol_info +{ + const char *strtab; + const Elf_W (Sym) *sym; + Elf_W (Addr) start_ip; +}; + +struct symbol_lookup_context +{ + unw_addr_space_t as; + unw_word_t ip; + struct elf_image *ei; + Elf_W (Addr) load_offset; + Elf_W (Addr) *min_dist; +}; + +struct symbol_callback_data +{ + char *buf; + size_t buf_len; +}; + +struct ip_range_callback_data +{ + Elf_W (Addr) *start_ip; + Elf_W (Addr) *end_ip; +}; + static Elf_W (Shdr)* -elf_w (section_table) (struct elf_image *ei) +elf_w (section_table) (const struct elf_image *ei) { Elf_W (Ehdr) *ehdr = ei->image; Elf_W (Off) soff; @@ -53,7 +81,7 @@ elf_w (section_table) (struct elf_image *ei) } static char* -elf_w (string_table) (struct elf_image *ei, int section) +elf_w (string_table) (const struct elf_image *ei, int section) { Elf_W (Ehdr) *ehdr = ei->image; Elf_W (Off) soff, str_soff; @@ -85,11 +113,127 @@ elf_w (string_table) (struct elf_image *ei, int section) } static int -elf_w (lookup_symbol) (unw_addr_space_t as, - unw_word_t ip, struct elf_image *ei, - Elf_W (Addr) load_offset, - char *buf, size_t buf_len, Elf_W (Addr) *min_dist) +elf_w (lookup_symbol_from_dynamic) (unw_addr_space_t as UNUSED, + const struct symbol_lookup_context *context, + int (*callback)(const struct symbol_lookup_context *context, + const struct symbol_info *syminfo, void *data), + void *data) + +{ + struct elf_image *ei = context->ei; + Elf_W (Addr) load_offset = context->load_offset; + Elf_W (Addr) file_offset = 0; + Elf_W (Ehdr) *ehdr = ei->image; + Elf_W (Sym) *sym = NULL, *symtab = NULL; + Elf_W (Phdr) *phdr; + Elf_W (Word) sym_num; + Elf_W (Word) *hash = NULL, *gnu_hash = NULL; + Elf_W (Addr) val; + const char *strtab = NULL; + int ret = -UNW_ENOINFO; + size_t i; + Elf_W(Dyn) *dyn = NULL; + + phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff); + for (i = 0; i < ehdr->e_phnum; ++i) + if (phdr[i].p_type == PT_PHDR) + { + file_offset = phdr[i].p_vaddr - phdr[i].p_offset; + } + else if (phdr[i].p_type == PT_DYNAMIC) + { + dyn = (Elf_W (Dyn) *) ((char *)ei->image + phdr[i].p_offset); + break; + } + + if (!dyn) + return -UNW_ENOINFO; + + for (; dyn->d_tag != DT_NULL; ++dyn) + { + switch (dyn->d_tag) + { + case DT_SYMTAB: + symtab = (Elf_W (Sym) *) ((char *) ei->image + dyn->d_un.d_ptr - file_offset); + break; + case DT_STRTAB: + strtab = (const char *) ((char *) ei->image + dyn->d_un.d_ptr - file_offset); + break; + case DT_HASH: + hash = (Elf_W (Word) *) ((char *) ei->image + dyn->d_un.d_ptr - file_offset); + break; + case DT_GNU_HASH: + gnu_hash = (Elf_W (Word) *) ((char *) ei->image + dyn->d_un.d_ptr - file_offset); + break; + default: + break; + } + } + + if (!symtab || !strtab || (!hash && !gnu_hash)) + return -UNW_ENOINFO; + + if (gnu_hash) + { + uint32_t *buckets = gnu_hash + 4 + (gnu_hash[2] * sizeof(size_t)/4); + uint32_t *hashval; + for (i = sym_num = 0; i < gnu_hash[0]; i++) + if (buckets[i] > sym_num) + sym_num = buckets[i]; + + if (sym_num) + { + hashval = buckets + gnu_hash[0] + (sym_num - gnu_hash[1]); + do sym_num++; + while (!(*hashval++ & 1)); + } + } + else + { + sym_num = hash[1]; + } + + for (i = 0; i < sym_num; ++i) + { + sym = &symtab[i]; + if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC + && sym->st_shndx != SHN_UNDEF) + { + val = sym->st_value; + if (sym->st_shndx != SHN_ABS) + val += load_offset; + if (tdep_get_func_addr (as, val, &val) < 0) + continue; + Debug (16, "0x%016lx info=0x%02x %s\n", + (long) val, sym->st_info, strtab + sym->st_name); + + /* as long as found one, the return will be success*/ + struct symbol_info syminfo = + { + .strtab = strtab, + .sym = sym, + .start_ip = val + }; + if ((*callback) (context, &syminfo, data) == UNW_ESUCCESS) + { + if (ret != UNW_ESUCCESS) + ret = UNW_ESUCCESS; + } + } + } + + return ret; +} + +static int +elf_w (lookup_symbol_closeness) (unw_addr_space_t as UNUSED, + const struct symbol_lookup_context *context, + int (*callback)(const struct symbol_lookup_context *context, + const struct symbol_info *syminfo, void *data), + void *data) { + struct elf_image *ei = context->ei; + Elf_W (Addr) load_offset = context->load_offset; size_t syment_size; Elf_W (Ehdr) *ehdr = ei->image; Elf_W (Sym) *sym, *symtab, *symtab_end; @@ -137,13 +281,17 @@ elf_w (lookup_symbol) (unw_addr_space_t as, Debug (16, "0x%016lx info=0x%02x %s\n", (long) val, sym->st_info, strtab + sym->st_name); - if ((Elf_W (Addr)) (ip - val) < *min_dist) + /* as long as found one, the return will be success*/ + struct symbol_info syminfo = { - *min_dist = (Elf_W (Addr)) (ip - val); - strncpy (buf, strtab + sym->st_name, buf_len); - buf[buf_len - 1] = '\0'; - ret = (strlen (strtab + sym->st_name) >= buf_len - ? -UNW_ENOMEM : 0); + .strtab = strtab, + .sym = sym, + .start_ip = val + }; + if ((*callback) (context, &syminfo, data) == UNW_ESUCCESS) + { + if (ret != UNW_ESUCCESS) + ret = UNW_ESUCCESS; } } } @@ -154,9 +302,110 @@ elf_w (lookup_symbol) (unw_addr_space_t as, } shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize); } + + if (ret != UNW_ESUCCESS) + ret = elf_w (lookup_symbol_from_dynamic) (as, context, callback, data); + + return ret; +} + +static int +elf_w (lookup_symbol_callback)(const struct symbol_lookup_context *context, + const struct symbol_info *syminfo, void *data) +{ + int ret = -UNW_ENOINFO; + struct symbol_callback_data *d = data; + + if (context->ip < syminfo->start_ip || + context->ip >= (syminfo->start_ip + syminfo->sym->st_size)) + return -UNW_ENOINFO; + + if ((Elf_W (Addr)) (context->ip - syminfo->start_ip) < *(context->min_dist)) + { + *(context->min_dist) = (Elf_W (Addr)) (context->ip - syminfo->start_ip); + Debug (1, "candidate sym: %s@0x%lx\n", syminfo->strtab + syminfo->sym->st_name, syminfo->start_ip); + strncpy (d->buf, syminfo->strtab + syminfo->sym->st_name, d->buf_len); + d->buf[d->buf_len - 1] = '\0'; + ret = (strlen (syminfo->strtab + syminfo->sym->st_name) >= d->buf_len + ? -UNW_ENOMEM : UNW_ESUCCESS); + } + + return ret; +} + +static int +elf_w (lookup_symbol) (unw_addr_space_t as, + unw_word_t ip, struct elf_image *ei, + Elf_W (Addr) load_offset, + char *buf, size_t buf_len, Elf_W (Addr) *min_dist) +{ + struct symbol_lookup_context context = + { + .as = as, + .ip = ip, + .ei = ei, + .load_offset = load_offset, + .min_dist = min_dist, + }; + struct symbol_callback_data data = + { + .buf = buf, + .buf_len = buf_len, + }; + return elf_w (lookup_symbol_closeness) (as, + &context, + elf_w (lookup_symbol_callback), + &data); +} + +static int +elf_w (lookup_ip_range_callback)(const struct symbol_lookup_context *context, + const struct symbol_info *syminfo, void *data) +{ + int ret = -UNW_ENOINFO; + struct ip_range_callback_data *d = data; + + if (context->ip < syminfo->start_ip || + context->ip >= (syminfo->start_ip + syminfo->sym->st_size)) + return -UNW_ENOINFO; + + if ((Elf_W (Addr)) (context->ip - syminfo->start_ip) < *(context->min_dist)) + { + *(context->min_dist) = (Elf_W (Addr)) (context->ip - syminfo->start_ip); + *(d->start_ip) = syminfo->start_ip; + *(d->end_ip) = syminfo->start_ip + syminfo->sym->st_size; + + ret = UNW_ESUCCESS; + } + return ret; } +static int +elf_w (lookup_ip_range)(unw_addr_space_t as, + unw_word_t ip, struct elf_image *ei, + Elf_W (Addr) load_offset, Elf_W (Addr) *start_ip, + Elf_W (Addr) *end_ip, Elf_W (Addr) *min_dist) +{ + struct symbol_lookup_context context = + { + .as = as, + .ip = ip, + .ei = ei, + .load_offset = load_offset, + .min_dist = min_dist + }; + struct ip_range_callback_data data = + { + .start_ip = start_ip, + .end_ip = end_ip + }; + return elf_w (lookup_symbol_closeness) (as, + &context, + elf_w (lookup_ip_range_callback), + &data); +} + static Elf_W (Addr) elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase) { @@ -168,7 +417,7 @@ elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase) // PT_LOAD program headers p_offset however is not guaranteed to be aligned on a // page size, ld.lld generate libraries where this is not the case. So we must // make sure we compare both values with the same alignment. - unsigned long pagesize_alignment_mask = ~(((unsigned long)getpagesize()) - 1UL); + unsigned long pagesize_alignment_mask = ~(unw_page_size - 1UL); ehdr = ei->image; phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff); @@ -184,8 +433,62 @@ elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase) } #if HAVE_LZMA + +#define XZ_MAX_ALLOCS 16 +struct xz_allocator_data { + struct { + void *ptr; + size_t size; + } allocations[XZ_MAX_ALLOCS]; + uint8_t n_allocs; +}; + +static void* +xz_alloc (void *opaque, size_t nmemb, size_t size) +{ + struct xz_allocator_data *data = opaque; + if (XZ_MAX_ALLOCS == data->n_allocs) + return NULL; + size = UNW_ALIGN(size * nmemb, unw_page_size); + void *ptr; + GET_MEMORY (ptr, size); + if (!ptr) return ptr; + data->allocations[data->n_allocs].ptr = ptr; + data->allocations[data->n_allocs].size = size; + ++data->n_allocs; + return ptr; +} + +static void +xz_free (void *opaque, void *ptr) +{ + struct xz_allocator_data *data = opaque; + for (uint8_t i = data->n_allocs; i-- > 0;) + { + if (data->allocations[i].ptr == ptr) + { + mi_munmap (ptr, data->allocations[i].size); + --data->n_allocs; + if (i != data->n_allocs) + { + data->allocations[i] = data->allocations[data->n_allocs]; + } + return; + } + } +} + +static void +xz_free_all (struct xz_allocator_data *data) +{ + while (data->n_allocs-- > 0) + { + mi_munmap (data->allocations[data->n_allocs].ptr, data->allocations[data->n_allocs].size); + } +} + static size_t -xz_uncompressed_size (uint8_t *compressed, size_t length) +xz_uncompressed_size (lzma_allocator *xz_allocator, uint8_t *compressed, size_t length) { uint64_t memlimit = UINT64_MAX; size_t ret = 0, pos = 0; @@ -203,7 +506,7 @@ xz_uncompressed_size (uint8_t *compressed, size_t length) return 0; uint8_t *indexdata = footer - options.backward_size; - if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata, + if (lzma_index_buffer_decode (&index, &memlimit, xz_allocator, indexdata, &pos, options.backward_size) != LZMA_OK) return 0; @@ -212,7 +515,7 @@ xz_uncompressed_size (uint8_t *compressed, size_t length) ret = lzma_index_uncompressed_size (index); } - lzma_index_end (index, NULL); + lzma_index_end (index, xz_allocator); return ret; } @@ -224,6 +527,14 @@ elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi) uint64_t memlimit = UINT64_MAX; /* no memory limit */ size_t compressed_len, uncompressed_len; + struct xz_allocator_data allocator_data; + lzma_allocator xz_allocator = + { + .alloc = xz_alloc, + .free = xz_free, + .opaque = &allocator_data + }; + shdr = elf_w (find_section) (ei, ".gnu_debugdata"); if (!shdr) return 0; @@ -231,29 +542,34 @@ elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi) compressed = ((uint8_t *) ei->image) + shdr->sh_offset; compressed_len = shdr->sh_size; - uncompressed_len = xz_uncompressed_size (compressed, compressed_len); + uncompressed_len = xz_uncompressed_size (&xz_allocator, compressed, compressed_len); if (uncompressed_len == 0) { + xz_free_all (&allocator_data); Debug (1, "invalid .gnu_debugdata contents\n"); return 0; } mdi->size = uncompressed_len; - mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + GET_MEMORY (mdi->image, uncompressed_len); - if (mdi->image == MAP_FAILED) - return 0; + if (!mdi->image) + { + xz_free_all (&allocator_data); + return 0; + } size_t in_pos = 0, out_pos = 0; lzma_ret lret; - lret = lzma_stream_buffer_decode (&memlimit, 0, NULL, + lret = lzma_stream_buffer_decode (&memlimit, 0, &xz_allocator, compressed, &in_pos, compressed_len, mdi->image, &out_pos, mdi->size); + xz_free_all (&allocator_data); + if (lret != LZMA_OK) { Debug (1, "LZMA decompression failed: %d\n", lret); - munmap (mdi->image, mdi->size); + mi_munmap (mdi->image, mdi->size); return 0; } @@ -261,7 +577,7 @@ elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi) } #else static int -elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi) +elf_w (extract_minidebuginfo) (struct elf_image *ei UNUSED, struct elf_image *mdi UNUSED) { return 0; } @@ -299,7 +615,7 @@ elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei, ret = ret_mdi; } - munmap (mdi.image, mdi.size); + mi_munmap (mdi.image, mdi.size); } if (min_dist >= ei->size) @@ -322,20 +638,97 @@ elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip, if (ret < 0) return ret; - ret = elf_w (load_debuglink) (file, &ei, 1); + ret = elf_w (load_debuginfo) (file, &ei, 1); if (ret < 0) return ret; ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, ip, buf, buf_len, offp); - munmap (ei.image, ei.size); + mi_munmap (ei.image, ei.size); + ei.image = NULL; + + return ret; +} + +HIDDEN int +elf_w (get_proc_ip_range_in_image) (unw_addr_space_t as, struct elf_image *ei, + unsigned long segbase, + unw_word_t ip, + unw_word_t *start, unw_word_t *end) +{ + Elf_W (Addr) load_offset; + Elf_W (Addr) min_dist = ~(Elf_W (Addr))0; + int ret; + + load_offset = elf_w (get_load_offset) (ei, segbase); + ret = elf_w (lookup_ip_range) (as, ip, ei, load_offset, start, end, &min_dist); + + /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in + there as well and replace the previously found if it is closer. */ + struct elf_image mdi; + if (elf_w (extract_minidebuginfo) (ei, &mdi)) + { + int ret_mdi = elf_w (lookup_ip_range) (as, ip, &mdi, load_offset, start, + end, &min_dist); + + /* Closer symbol was found (possibly truncated). */ + if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM) + { + ret = ret_mdi; + } + + mi_munmap (mdi.image, mdi.size); + } + + if (min_dist >= ei->size) + return -UNW_ENOINFO; /* not found */ + return ret; +} + +HIDDEN int +elf_w (get_proc_ip_range) (unw_addr_space_t as, pid_t pid, unw_word_t ip, + unw_word_t *start, unw_word_t *end) +{ + unsigned long segbase, mapoff; + struct elf_image ei; + int ret; + char file[PATH_MAX]; + + ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, file, PATH_MAX); + if (ret < 0) + return ret; + + ret = elf_w (load_debuginfo) (file, &ei, 1); + if (ret < 0) + return ret; + + ret = elf_w (get_proc_ip_range_in_image) (as, &ei, segbase, ip, start, end); + + mi_munmap (ei.image, ei.size); ei.image = NULL; return ret; } +HIDDEN int +elf_w (get_elf_filename) (unw_addr_space_t as, pid_t pid, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp) +{ + unsigned long segbase, mapoff; + int ret = UNW_ESUCCESS; + + // use NULL to no map elf image + ret = tdep_get_elf_image (NULL, pid, ip, &segbase, &mapoff, buf, buf_len); + if (ret < 0) + return ret; + + if (offp) + *offp = ip - segbase + mapoff; + return ret; +} + HIDDEN Elf_W (Shdr)* -elf_w (find_section) (struct elf_image *ei, const char* secname) +elf_w (find_section) (const struct elf_image *ei, const char* secname) { Elf_W (Ehdr) *ehdr = ei->image; Elf_W (Shdr) *shdr; @@ -378,6 +771,95 @@ elf_w (find_section) (struct elf_image *ei, const char* secname) return 0; } + +static char * +elf_w (add_hex_byte) (char *str, uint8_t byte) +{ + const char hex[] = "0123456789abcdef"; + + *str++ = hex[byte >> 4]; + *str++ = hex[byte & 0xf]; + *str = 0; + + return str; +} + + +static int +elf_w (find_build_id_path) (const struct elf_image *ei, char *path, unsigned path_len) +{ +/* + * build-id is only available on GNU plaforms. So on non-GNU platforms this + * function just returns fail (-1). + */ +#if defined(ELF_NOTE_GNU) && defined(NT_GNU_BUILD_ID) + const Elf_W (Ehdr) *ehdr = ei->image; + const Elf_W (Phdr) *phdr; + unsigned i; + + if (!elf_w (valid_object) (ei)) + return -1; + + phdr = (Elf_W (Phdr) *) ((uint8_t *) ehdr + ehdr->e_phoff); + + for (i = 0; i < ehdr->e_phnum; ++i, phdr = (const Elf_W (Phdr) *) (((const uint8_t *) phdr) + ehdr->e_phentsize)) + { + const uint8_t *notes; + const uint8_t *notes_end; + + /* The build-id is in a note section */ + if (phdr->p_type != PT_NOTE) + continue; + + notes = ((const uint8_t *) ehdr) + phdr->p_offset; + notes_end = notes + phdr->p_memsz; + + while(notes < notes_end) + { + const char prefix[] = "/usr/lib/debug/.build-id/"; + + /* See "man 5 elf" for notes about alignment in Nhdr */ + const Elf_W(Nhdr) *nhdr = (const ElfW(Nhdr) *) notes; + const ElfW(Word) namesz = nhdr->n_namesz; + const ElfW(Word) descsz = nhdr->n_descsz; + const ElfW(Word) nameasz = UNW_ALIGN(namesz, 4); /* Aligned size */ + const char *name = (const char *) (nhdr + 1); + const uint8_t *desc = (const uint8_t *) name + nameasz; + unsigned j; + + notes += sizeof(*nhdr) + nameasz + UNW_ALIGN(descsz, 4); + + if ((namesz != sizeof(ELF_NOTE_GNU)) || /* Spec says must be "GNU" with a NULL */ + (nhdr->n_type != NT_GNU_BUILD_ID) || /* Spec says must be NT_GNU_BUILD_ID */ + (strcmp(name, ELF_NOTE_GNU) != 0)) /* Must be "GNU" with NULL termination */ + continue; + + /* Validate that we have enough space */ + if (path_len < (sizeof(prefix) + /* Path prefix inc NULL */ + 2 + /* Subdirectory */ + 1 + /* Directory separator */ + (2 * (descsz - 1)) + /* Leaf filename */ + 6)) /* .debug extension */ + return -1; + + memcpy(path, prefix, sizeof(prefix)); + + path = elf_w (add_hex_byte) (path + sizeof(prefix) - 1, *desc); + *path++ = '/'; + + for(j = 1, ++desc; j < descsz; ++j, ++desc) + path = elf_w (add_hex_byte) (path, *desc); + + strcat(path, ".debug"); + + return 0; + } + } +#endif /* defined(ELF_NOTE_GNU) */ + + return -1; +} + /* Load a debug section, following .gnu_debuglink if appropriate * Loads ei from file if not already mapped. * If is_local, will also search sys directories /usr/local/dbg @@ -386,18 +868,19 @@ elf_w (find_section) (struct elf_image *ei, const char* secname) * ei will be mapped to file or the located .gnu_debuglink from file */ HIDDEN int -elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local) +elf_w (load_debuginfo) (const char* file, struct elf_image *ei, int is_local) { int ret; Elf_W (Shdr) *shdr; Elf_W (Ehdr) *prev_image; off_t prev_size; + char path[PATH_MAX]; if (!ei->image) { ret = elf_map_image(ei, file); if (ret) - return ret; + return ret; } prev_image = ei->image; @@ -408,13 +891,27 @@ elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local) return 0; } + ret = elf_w (find_build_id_path) (ei, path, sizeof(path)); + if (ret == 0) + { + ei->image = NULL; + + ret = elf_w (load_debuginfo) (path, ei, -1); + if (ret == 0) + { + mi_munmap (prev_image, prev_size); + return 0; + } + + ei->image = prev_image; + ei->size = prev_size; + } + shdr = elf_w (find_section) (ei, ".gnu_debuglink"); if (shdr) { if (shdr->sh_size >= PATH_MAX || (shdr->sh_offset + shdr->sh_size > ei->size)) - { - return 0; - } + return 0; { char linkbuf[shdr->sh_size]; @@ -445,14 +942,14 @@ elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local) strcpy (newname, basedir); strcat (newname, "/"); strcat (newname, linkbuf); - ret = elf_w (load_debuglink) (newname, ei, -1); + ret = elf_w (load_debuginfo) (newname, ei, -1); if (ret == -1) { strcpy (newname, basedir); strcat (newname, "/.debug/"); strcat (newname, linkbuf); - ret = elf_w (load_debuglink) (newname, ei, -1); + ret = elf_w (load_debuginfo) (newname, ei, -1); } if (ret == -1 && is_local == 1) @@ -461,7 +958,7 @@ elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local) strcat (newname, basedir); strcat (newname, "/"); strcat (newname, linkbuf); - ret = elf_w (load_debuglink) (newname, ei, -1); + ret = elf_w (load_debuginfo) (newname, ei, -1); } if (ret == -1) @@ -474,7 +971,7 @@ elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local) } else { - munmap (prev_image, prev_size); + mi_munmap (prev_image, prev_size); } return ret; diff --git a/src/native/external/libunwind/src/elfxx.h b/src/native/external/libunwind/src/elfxx.h index cd36eb42451b2a..ad82d9be867caf 100644 --- a/src/native/external/libunwind/src/elfxx.h +++ b/src/native/external/libunwind/src/elfxx.h @@ -53,11 +53,22 @@ extern int elf_w (get_proc_name_in_image) (unw_addr_space_t as, unw_word_t ip, char *buf, size_t buf_len, unw_word_t *offp); -extern Elf_W (Shdr)* elf_w (find_section) (struct elf_image *ei, const char* secname); -extern int elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local); +extern int elf_w (get_proc_ip_range) (unw_addr_space_t as, + pid_t pid, unw_word_t ip, + unw_word_t *start, unw_word_t *end); + +extern int elf_w (get_proc_ip_range_in_image) (unw_addr_space_t as, struct elf_image *ei, + unsigned long segbase, unw_word_t ip, + unw_word_t *start, unw_word_t *end); + +extern int elf_w (get_elf_filename) (unw_addr_space_t as, pid_t pid, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp); + +extern Elf_W (Shdr)* elf_w (find_section) (const struct elf_image *ei, const char* secname); +extern int elf_w (load_debuginfo) (const char* file, struct elf_image *ei, int is_local); static inline int -elf_w (valid_object) (struct elf_image *ei) +elf_w (valid_object) (const struct elf_image *ei) { if (ei->size <= EI_VERSION) return 0; @@ -85,14 +96,14 @@ elf_map_image (struct elf_image *ei, const char *path) } ei->size = stat.st_size; - ei->image = mmap (NULL, ei->size, PROT_READ, MAP_PRIVATE, fd, 0); + ei->image = mi_mmap (NULL, ei->size, PROT_READ, MAP_PRIVATE, fd, 0); close (fd); if (ei->image == MAP_FAILED) return -1; if (!elf_w (valid_object) (ei)) { - munmap(ei->image, ei->size); + mi_munmap(ei->image, ei->size); return -1; } diff --git a/src/native/external/libunwind/src/hppa/Ginit.c b/src/native/external/libunwind/src/hppa/Ginit.c index 265455a68c82a1..5f06b17f71b6da 100644 --- a/src/native/external/libunwind/src/hppa/Ginit.c +++ b/src/native/external/libunwind/src/hppa/Ginit.c @@ -91,11 +91,11 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (write) { Debug (12, "mem[%x] <- %x\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "mem[%x] -> %x\n", addr, *val); } return 0; @@ -117,12 +117,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %x\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %x\n", unw_regname (reg), *val); } return 0; @@ -174,10 +174,23 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf32_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void hppa_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -187,6 +200,7 @@ hppa_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = hppa_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/ia64/Ginit.c b/src/native/external/libunwind/src/ia64/Ginit.c index 4c70a33ccd8d13..91750bee0ed5f3 100644 --- a/src/native/external/libunwind/src/ia64/Ginit.c +++ b/src/native/external/libunwind/src/ia64/Ginit.c @@ -80,11 +80,11 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (write) { Debug (12, "mem[%lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "mem[%lx] -> %lx\n", addr, *val); } return 0; @@ -293,7 +293,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %lx\n", unw_regname (reg), *val); } return 0; @@ -352,6 +352,14 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf64_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void ia64_local_addr_space_init (void) { @@ -361,6 +369,11 @@ ia64_local_addr_space_init (void) local_addr_space.abi = ABI_LINUX; #elif defined(__hpux) local_addr_space.abi = ABI_HPUX; +#endif +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif #endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = tdep_find_proc_info; @@ -371,6 +384,7 @@ ia64_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = ia64_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/ia64/Gtables.c b/src/native/external/libunwind/src/ia64/Gtables.c index e6ac519780d583..3eed66cba5a836 100644 --- a/src/native/external/libunwind/src/ia64/Gtables.c +++ b/src/native/external/libunwind/src/ia64/Gtables.c @@ -624,7 +624,7 @@ validate_cache (unw_addr_space_t as) int ret; SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); - ret = dl_iterate_phdr (check_callback, as); + ret = as->iterate_phdr_function (check_callback, as); SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); return ret; } @@ -655,7 +655,7 @@ tdep_find_proc_info (unw_addr_space_t as, unw_word_t ip, di.u.ti.segbase = ip; /* this is cheap... */ SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask); - ret = dl_iterate_phdr (callback, &di); + ret = as->iterate_phdr_function (callback, &di); SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL); if (ret <= 0) diff --git a/src/native/external/libunwind/src/loongarch64/Gglobal.c b/src/native/external/libunwind/src/loongarch64/Gglobal.c index e770e09d32604b..cc5a5abbdffeba 100644 --- a/src/native/external/libunwind/src/loongarch64/Gglobal.c +++ b/src/native/external/libunwind/src/loongarch64/Gglobal.c @@ -47,8 +47,6 @@ tdep_init (void) dwarf_init (); #ifndef UNW_REMOTE_ONLY - tdep_init_mem_validate (); - loongarch64_local_addr_space_init (); #endif tdep_init_done = 1; /* signal that we're initialized... */ diff --git a/src/native/external/libunwind/src/loongarch64/Ginit.c b/src/native/external/libunwind/src/loongarch64/Ginit.c index 8994e2eea090d2..75fbe4c997ce3d 100644 --- a/src/native/external/libunwind/src/loongarch64/Ginit.c +++ b/src/native/external/libunwind/src/loongarch64/Ginit.c @@ -84,232 +84,6 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, } -static int mem_validate_pipe[2] = {-1, -1}; - -#ifdef HAVE_PIPE2 -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); -} -#else -static inline void -set_pipe_flags (int fd) -{ - int fd_flags = fcntl (fd, F_GETFD, 0); - int status_flags = fcntl (fd, F_GETFL, 0); - - fd_flags |= FD_CLOEXEC; - fcntl (fd, F_SETFD, fd_flags); - - status_flags |= O_NONBLOCK; - fcntl (fd, F_SETFL, status_flags); -} - -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe (pipefd); - set_pipe_flags(pipefd[0]); - set_pipe_flags(pipefd[1]); -} -#endif - -static inline void -open_pipe (void) -{ - if (mem_validate_pipe[0] != -1) - close (mem_validate_pipe[0]); - if (mem_validate_pipe[1] != -1) - close (mem_validate_pipe[1]); - - do_pipe2 (mem_validate_pipe); -} - -ALWAYS_INLINE -static int -write_validate (void *addr) -{ - int ret = -1; - ssize_t bytes = 0; - - do - { - char buf; - bytes = read (mem_validate_pipe[0], &buf, 1); - } - while ( errno == EINTR ); - - int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK); - if (!valid_read) - { - // re-open closed pipe - open_pipe (); - } - - do - { - ret = write (mem_validate_pipe[1], addr, 1); - } - while ( errno == EINTR ); - - return ret; -} - -static int (*mem_validate_func) (void *addr, size_t len); -static int msync_validate (void *addr, size_t len) -{ - if (msync (addr, len, MS_ASYNC) != 0) - { - return -1; - } - - return write_validate (addr); -} - -#ifdef HAVE_MINCORE -static int mincore_validate (void *addr, size_t len) -{ - unsigned char mvec[2]; /* Unaligned access may cross page boundary */ - - /* mincore could fail with EAGAIN but we conservatively return -1 - instead of looping. */ - if (mincore (addr, len, (unsigned char *)mvec) != 0) - { - return -1; - } - - return write_validate (addr); -} -#endif - -/* Initialise memory validation method. On linux kernels <2.6.21, - mincore() returns incorrect value for MAP_PRIVATE mappings, - such as stacks. If mincore() was available at compile time, - check if we can actually use it. If not, use msync() instead. */ -HIDDEN void -tdep_init_mem_validate (void) -{ - open_pipe (); - -#ifdef HAVE_MINCORE - unsigned char present = 1; - size_t len = unw_page_size; - unw_word_t addr = uwn_page_start((unw_word_t)&present); - unsigned char mvec[1]; - int ret; - while ((ret = mincore ((void*)addr, len, (unsigned char *)mvec)) == -1 && - errno == EAGAIN) - { - } - if (ret == 0) - { - Debug(1, "using mincore to validate memory\n"); - mem_validate_func = mincore_validate; - } - else -#endif - { - Debug(1, "using msync to validate memory\n"); - mem_validate_func = msync_validate; - } -} - -/* Cache of already validated addresses */ -#define NLGA 4 -#if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD -// thread-local variant -static _Thread_local unw_word_t last_good_addr[NLGA]; -static _Thread_local int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == last_good_addr[i]) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (last_good_addr[victim] == 0) { - last_good_addr[victim] = addr; - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; -} - -#else -// global, thread safe variant -static _Atomic unw_word_t last_good_addr[NLGA]; -static _Atomic int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == atomic_load(&last_good_addr[i])) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = atomic_load(&lga_victim); - unw_word_t zero = 0; - for (i = 0; i < NLGA; i++) { - if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) { - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - atomic_store(&last_good_addr[victim], addr); - victim = (victim + 1) % NLGA; - atomic_store(&lga_victim, victim); -} -#endif - -static int -validate_mem (unw_word_t addr) -{ - size_t len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - if (is_cached_valid_mem(addr)) - return 0; - - if (mem_validate_func ((void *) addr, len) == -1) - return -1; - - cache_valid_mem(addr); - - return 0; -} - static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) @@ -324,7 +98,7 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, /* validate address */ const struct cursor *c = (const struct cursor *)arg; if (likely (c != NULL) && unlikely (c->validate) - && unlikely (validate_mem (addr))) { + && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) { Debug (16, "mem[%016lx] -> invalid\n", addr); return -1; } @@ -380,11 +154,24 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return elf_w (get_elf_filename) (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void loongarch64_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNW_CACHE_GLOBAL; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -394,6 +181,7 @@ loongarch64_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = loongarch64_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/mi/Gaddress_validator.c b/src/native/external/libunwind/src/mi/Gaddress_validator.c new file mode 100644 index 00000000000000..aaf5a0941214fa --- /dev/null +++ b/src/native/external/libunwind/src/mi/Gaddress_validator.c @@ -0,0 +1,302 @@ +/* + * Contributed by Stephen M. Webb + * + * This file is part of libunwind, a platform-independent unwind library. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "libunwind_i.h" + + +#ifdef UNW_REMOTE_ONLY +bool +unw_address_is_valid(UNUSED unw_word_t addr, UNUSED size_t len) +{ + Debug(1, "remote-only invoked\n"); + return false; +} + +#else /* !UNW_REMOTE_ONLY */ + +#include + + +static atomic_flag _unw_address_validator_initialized = ATOMIC_FLAG_INIT; +static int _mem_validate_pipe[2] = {-1, -1}; + +#ifdef HAVE_PIPE2 +static int +_do_pipe2 (int pipefd[2]) +{ + return pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); +} +#else +static void +_set_pipe_flags (int fd) +{ + int fd_flags = fcntl (fd, F_GETFD, 0); + int status_flags = fcntl (fd, F_GETFL, 0); + + fd_flags |= FD_CLOEXEC; + fcntl (fd, F_SETFD, fd_flags); + + status_flags |= O_NONBLOCK; + fcntl (fd, F_SETFL, status_flags); +} + +static int +_do_pipe2 (int pipefd[2]) +{ + if (pipe (pipefd) < 0) + { + return -1; + } + _set_pipe_flags(pipefd[0]); + _set_pipe_flags(pipefd[1]); +} +#endif + + +static int +_open_pipe (void) +{ + if (_mem_validate_pipe[0] != -1) + close (_mem_validate_pipe[0]); + if (_mem_validate_pipe[1] != -1) + close (_mem_validate_pipe[1]); + + return _do_pipe2 (_mem_validate_pipe); +} + + +/** + * Test is a memory address is valid by trying to write from it + * @param[in] addr The address to validate + * + * @returns true if the memory address is valid (readable), false otherwise. + * + * This check works by using the address as a (one-byte) buffer in a + * write-to-pipe operation. The write will fail if the memory is not in the + * process's address space and marked as readable. The read will force the page + * to be swapped in if it's not already there. + */ +static bool +_write_validate (unw_word_t addr) +{ + int ret = -1; + ssize_t bytes = 0; + + if (unlikely (!atomic_flag_test_and_set(&_unw_address_validator_initialized))) + { + if (_open_pipe () != 0) + { + return false; + } + } + + do + { + char buf; + bytes = read (_mem_validate_pipe[0], &buf, 1); + } + while ( errno == EINTR ); + + if (!(bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK)) + { + // re-open closed pipe + if (_open_pipe () != 0) + { + return false; + } + } + + do + { +#ifdef HAVE_SYS_SYSCALL_H + /* use syscall insteadof write() so that ASAN does not complain */ + ret = syscall (SYS_write, _mem_validate_pipe[1], addr, 1); +#else + ret = write (_mem_validate_pipe[1], (void *)addr, 1); +#endif + } + while ( errno == EINTR ); + + return ret > 0; +} + + +/* Cache of already validated addresses */ +enum { NLGA = 4 }; + +#if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD +// thread-local variant +static _Thread_local unw_word_t last_good_addr[NLGA]; +static _Thread_local int lga_victim; + + +static bool +_is_cached_valid_mem(unw_word_t page_addr) +{ + int i; + for (i = 0; i < NLGA; i++) + { + if (page_addr == last_good_addr[i]) + return true; + } + return false; +} + + +static void +_cache_valid_mem(unw_word_t page_addr) +{ + int i, victim; + victim = lga_victim; + for (i = 0; i < NLGA; i++) + { + if (last_good_addr[victim] == 0) + { + last_good_addr[victim] = page_addr; + return; + } + victim = (victim + 1) % NLGA; + } + + /* All slots full. Evict the victim. */ + last_good_addr[victim] = page_addr; + victim = (victim + 1) % NLGA; + lga_victim = victim; +} + +#else +// global, thread safe variant +static _Atomic unw_word_t last_good_addr[NLGA]; +static _Atomic int lga_victim; + + +static bool +_is_cached_valid_mem(unw_word_t page_addr) +{ + int i; + for (i = 0; i < NLGA; i++) + { + if (page_addr == atomic_load(&last_good_addr[i])) + return true; + } + return false; +} + + +/** + * Adds a known-valid page address to the cache. + * + * This implementation is racy as all get-out but the worst case is that cached + * address get lost, forcing extra unnecessary validation checks. All of the + * atomic operations don't matter because of TOCTOU races. + */ +static void +_cache_valid_mem(unw_word_t page_addr) +{ + unw_word_t zero = 0; + int victim = atomic_load(&lga_victim); + for (int i = 0; i < NLGA; i++) + { + if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, page_addr)) + { + return; + } + victim = (victim + 1) % NLGA; + } + + /* All slots full. Evict the victim. */ + atomic_store(&last_good_addr[victim], page_addr); + victim = (victim + 1) % NLGA; + atomic_store(&lga_victim, victim); +} +#endif + + +/** + * Validate an address is readable + * @param[in] addr The (starting) address of the memory range to validate + * @param[in] len The size of the memory range to validate in bytes + * + * Validates the memory range from @p addr to (@p addr + @p len - 1) is + * readable. Since the granularity of memory readability is the page, only one + * byte needs to be validated per page for each page starting at @p addr and + * encompassing @p len bytes. Only the first address of each page is checked. + * + * @returns true if the memory is readable, false otherwise. + */ +bool +unw_address_is_valid(unw_word_t addr, size_t len) +{ + if (len == 0) + return true; + + /* + * Find the starting address of the page containing the start of the range. + */ + unw_word_t start_page_addr = unw_page_start (addr); + + /* + * Bounds check on bottom of memory: first page is always deemed inaccessible. + * This is potentially incorrect on an embedded system, especially one running + * on bare metal with no VMM, but the check has always been here and no one + * has complained. + */ + if (start_page_addr == 0) + return false; + + /* + * Bounds check on top of memory. Unsigned wraparound could be hazardous. + */ + if (addr > (UNW_WORD_MAX - len - unw_page_size)) + return false; + + /* + * Find the starting address of the page containing the end of the range. + */ + unw_word_t end_page_addr = unw_page_start (addr + (len - 1)) + unw_page_size; + + /* + * Step through each page and check if the first address in each is readable. + * The first non-readable page encountered means none of them in the given + * range can be considered readable. + */ + for (unw_word_t page_addr = start_page_addr; + page_addr < end_page_addr; + page_addr += unw_page_size) + { + if (!_is_cached_valid_mem(page_addr)) + { + if (!_write_validate (page_addr)) + { + Debug(1, "returning false\n"); + return false; + } + _cache_valid_mem(page_addr); + } + } + + return true; +} + +#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/native/external/libunwind/src/mi/Gdestroy_addr_space.c b/src/native/external/libunwind/src/mi/Gdestroy_addr_space.c index 504558e1a7880c..fa29ac9555a807 100644 --- a/src/native/external/libunwind/src/mi/Gdestroy_addr_space.c +++ b/src/native/external/libunwind/src/mi/Gdestroy_addr_space.c @@ -26,7 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "libunwind_i.h" void -unw_destroy_addr_space (unw_addr_space_t as) +unw_destroy_addr_space (unw_addr_space_t as UNUSED) { #ifndef UNW_LOCAL_ONLY # if UNW_DEBUG diff --git a/src/native/external/libunwind/src/mi/Gdyn-remote.c b/src/native/external/libunwind/src/mi/Gdyn-remote.c index 40a5ad8b5aba41..6d4ec1ecf869b3 100644 --- a/src/native/external/libunwind/src/mi/Gdyn-remote.c +++ b/src/native/external/libunwind/src/mi/Gdyn-remote.c @@ -286,8 +286,9 @@ unwi_dyn_remote_find_proc_info (unw_addr_space_t as, unw_word_t ip, } HIDDEN void -unwi_dyn_remote_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, - void *arg) +unwi_dyn_remote_put_unwind_info (unw_addr_space_t as UNUSED, + unw_proc_info_t *pi, + void *arg UNUSED) { if (!pi->unwind_info) return; diff --git a/src/native/external/libunwind/src/mi/Gfind_dynamic_proc_info.c b/src/native/external/libunwind/src/mi/Gfind_dynamic_proc_info.c index 2e7c62e5e8623e..dcd7f9de0dc572 100644 --- a/src/native/external/libunwind/src/mi/Gfind_dynamic_proc_info.c +++ b/src/native/external/libunwind/src/mi/Gfind_dynamic_proc_info.c @@ -63,8 +63,11 @@ local_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, #ifdef UNW_LOCAL_ONLY static inline int -remote_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, - int need_unwind_info, void *arg) +remote_find_proc_info (unw_addr_space_t as UNUSED, + unw_word_t ip UNUSED, + unw_proc_info_t *pi UNUSED, + int need_unwind_info UNUSED, + void *arg UNUSED) { return -UNW_ENOINFO; } diff --git a/src/native/external/libunwind/src/mi/Gget_elf_filename.c b/src/native/external/libunwind/src/mi/Gget_elf_filename.c new file mode 100644 index 00000000000000..97d0645164fe4f --- /dev/null +++ b/src/native/external/libunwind/src/mi/Gget_elf_filename.c @@ -0,0 +1,77 @@ +/* libunwind - a platform-independent unwind library +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "libunwind_i.h" +#include "remote.h" + +int +unw_get_elf_filename_by_ip (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + unw_accessors_t *a = unw_get_accessors_int (as); + unw_proc_info_t pi; + int ret; + + buf[0] = '\0'; /* always return a valid string, even if it's empty */ + + ret = unwi_find_dynamic_proc_info (as, ip, &pi, 1, arg); + if (ret == 0) + { + unwi_put_dynamic_unwind_info (as, &pi, arg); + return -UNW_ENOINFO; + } + + if (a->get_elf_filename) + return (*a->get_elf_filename) (as, ip, buf, buf_len, offp, arg); + + return -UNW_ENOINFO; +} + +int +unw_get_elf_filename (unw_cursor_t *cursor, char *buf, size_t buf_len, + unw_word_t *offp) +{ + struct cursor *c = (struct cursor *) cursor; + unw_word_t ip; + int error; + + ip = tdep_get_ip (c); +#if !defined(__ia64__) + if (c->dwarf.use_prev_instr) + { +#if defined(__arm__) + /* On arm, the least bit denotes thumb/arm mode, clear it. */ + ip &= ~(unw_word_t)0x1; +#endif + --ip; + } + +#endif + error = unw_get_elf_filename_by_ip (tdep_get_as (c), ip, buf, buf_len, offp, + tdep_get_as_arg (c)); +#if !defined(__ia64__) + if (c->dwarf.use_prev_instr && offp != NULL && error == 0) + *offp += 1; +#endif + return error; +} diff --git a/src/native/external/libunwind/src/mi/Gset_iterate_phdr_function.c b/src/native/external/libunwind/src/mi/Gset_iterate_phdr_function.c new file mode 100644 index 00000000000000..d56f1fb69ed5d3 --- /dev/null +++ b/src/native/external/libunwind/src/mi/Gset_iterate_phdr_function.c @@ -0,0 +1,23 @@ +#include "libunwind_i.h" + +//! Set an alternative function to use in place of dl_iterate_phdr. +/*! Suggested use is to specify an async-signal safe implementation. + * If not set (or set to NULL) the system dl_iterate_phdr will + * be used. */ +void +unw_set_iterate_phdr_function (unw_addr_space_t as, unw_iterate_phdr_func_t function) +{ + if (!tdep_init_done) + tdep_init (); + +#ifndef UNW_REMOTE_ONLY + if (function) + as->iterate_phdr_function = function; + else +# if defined(HAVE_DL_ITERATE_PHDR) + as->iterate_phdr_function = dl_iterate_phdr; +# else + as->iterate_phdr_function = NULL; +# endif +#endif +} diff --git a/src/native/external/libunwind/src/tilegx/Lcreate_addr_space.c b/src/native/external/libunwind/src/mi/Laddress_validator.c similarity index 77% rename from src/native/external/libunwind/src/tilegx/Lcreate_addr_space.c rename to src/native/external/libunwind/src/mi/Laddress_validator.c index 0f2dc6be901453..b810a3fea02d8c 100644 --- a/src/native/external/libunwind/src/tilegx/Lcreate_addr_space.c +++ b/src/native/external/libunwind/src/mi/Laddress_validator.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gcreate_addr_space.c" +#include "Gaddress_validator.c" #endif diff --git a/src/native/external/libunwind/src/tilegx/Lapply_reg_state.c b/src/native/external/libunwind/src/mi/Lget_elf_filename.c similarity index 78% rename from src/native/external/libunwind/src/tilegx/Lapply_reg_state.c rename to src/native/external/libunwind/src/mi/Lget_elf_filename.c index 7ebada480e5640..6f24fdbdf24759 100644 --- a/src/native/external/libunwind/src/tilegx/Lapply_reg_state.c +++ b/src/native/external/libunwind/src/mi/Lget_elf_filename.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gapply_reg_state.c" +#include "Gget_elf_filename.c" #endif diff --git a/src/native/external/libunwind/src/tilegx/Lget_proc_info.c b/src/native/external/libunwind/src/mi/Lset_iterate_phdr_function.c similarity index 73% rename from src/native/external/libunwind/src/tilegx/Lget_proc_info.c rename to src/native/external/libunwind/src/mi/Lset_iterate_phdr_function.c index 69028b019fcd51..67cef9519c8375 100644 --- a/src/native/external/libunwind/src/tilegx/Lget_proc_info.c +++ b/src/native/external/libunwind/src/mi/Lset_iterate_phdr_function.c @@ -1,5 +1,5 @@ #define UNW_LOCAL_ONLY #include #if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gget_proc_info.c" +#include "Gset_iterate_phdr_function.c" #endif diff --git a/src/native/external/libunwind/src/mi/flush_cache.c b/src/native/external/libunwind/src/mi/flush_cache.c index 55ee8a3305edc7..0ea78be89237e5 100644 --- a/src/native/external/libunwind/src/mi/flush_cache.c +++ b/src/native/external/libunwind/src/mi/flush_cache.c @@ -27,7 +27,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include void -unw_flush_cache (unw_addr_space_t as, unw_word_t lo, unw_word_t hi) +unw_flush_cache (unw_addr_space_t as, unw_word_t lo UNUSED, unw_word_t hi UNUSED) { #if !UNW_TARGET_IA64 struct unw_debug_frame_list *w = as->debug_frames; @@ -37,10 +37,10 @@ unw_flush_cache (unw_addr_space_t as, unw_word_t lo, unw_word_t hi) struct unw_debug_frame_list *n = w->next; if (w->index) - munmap (w->index, w->index_size); + mi_munmap (w->index, w->index_size); - munmap (w->debug_frame, w->debug_frame_size); - munmap (w, sizeof (*w)); + mi_munmap (w->debug_frame, w->debug_frame_size); + mi_munmap (w, sizeof (*w)); w = n; } as->debug_frames = NULL; diff --git a/src/native/external/libunwind/src/mi/mempool.c b/src/native/external/libunwind/src/mi/mempool.c index 7c5d27d0c2c309..a45f19e8c25dfc 100644 --- a/src/native/external/libunwind/src/mi/mempool.c +++ b/src/native/external/libunwind/src/mi/mempool.c @@ -43,7 +43,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ static alignas(MAX_ALIGN) char sos_memory[SOS_MEMORY_SIZE]; static _Atomic size_t sos_memory_freepos = 0; -static size_t pg_size; HIDDEN void * sos_alloc (size_t size) @@ -94,7 +93,7 @@ expand (struct mempool *pool) GET_MEMORY (mem, size); if (!mem) { - size = UNW_ALIGN(pool->obj_size, pg_size); + size = UNW_ALIGN(pool->obj_size, unw_page_size); GET_MEMORY (mem, size); if (!mem) { @@ -109,9 +108,6 @@ expand (struct mempool *pool) HIDDEN void mempool_init (struct mempool *pool, size_t obj_size, size_t reserve) { - if (pg_size == 0) - pg_size = getpagesize (); - memset (pool, 0, sizeof (*pool)); lock_init (&pool->lock); @@ -121,14 +117,14 @@ mempool_init (struct mempool *pool, size_t obj_size, size_t reserve) if (!reserve) { - reserve = pg_size / obj_size / 4; + reserve = unw_page_size / obj_size / 4; if (!reserve) reserve = 16; } pool->obj_size = obj_size; pool->reserve = reserve; - pool->chunk_size = UNW_ALIGN(2*reserve*obj_size, pg_size); + pool->chunk_size = UNW_ALIGN(2*reserve*obj_size, unw_page_size); expand (pool); } diff --git a/src/native/external/libunwind/src/mips/Ginit.c b/src/native/external/libunwind/src/mips/Ginit.c index 84a34352016b8b..748419d20fa4be 100644 --- a/src/native/external/libunwind/src/mips/Ginit.c +++ b/src/native/external/libunwind/src/mips/Ginit.c @@ -179,6 +179,15 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + + return elf_w (get_elf_filename) (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void mips_local_addr_space_init (void) { @@ -194,6 +203,11 @@ mips_local_addr_space_init (void) # error Unsupported ABI #endif local_addr_space.addr_size = sizeof (void *); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -203,6 +217,7 @@ mips_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = NULL; /* mips_local_resume? FIXME! */ local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/nto/unw_nto_access_fpreg.c b/src/native/external/libunwind/src/nto/unw_nto_access_fpreg.c new file mode 100644 index 00000000000000..c1d89c4b7dd338 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_access_fpreg.c @@ -0,0 +1,43 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "libunwind-nto.h" +#include "unw_nto_internal.h" + +#include + + +int unw_nto_access_fpreg (unw_addr_space_t as, + unw_regnum_t regnum, + unw_fpreg_t *valp, int write, + void *arg) +{ + if (!write) + { + memset (valp, 0, sizeof (*valp)); + } + + return 0; +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_access_mem.c b/src/native/external/libunwind/src/nto/unw_nto_access_mem.c new file mode 100644 index 00000000000000..670c17b26f4019 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_access_mem.c @@ -0,0 +1,79 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" +#include "os-qnx.h" + +#include +#include +#include +#include + + +/** + * Read/write data from/to the target address space + * + * @todo save opened fd in unw_nto_internal_t + */ +int unw_nto_access_mem (unw_addr_space_t as, + unw_word_t addr, + unw_word_t *valp, + int write, + void *arg) +{ + int ret = -UNW_ENOINFO; + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + int as_fd = unw_nto_procfs_open_as (uni->pid); + + if (as_fd < 0) + { + Debug (0, "error %d opening as file: %s\n", errno, strerror (errno)); + return ret; + } + + if (lseek (as_fd, (off_t)addr, SEEK_SET) == -1) + { + Debug (0, "error %d in lseek(%" PRIxPTR "): %s\n", errno, addr, strerror (errno)); + close (as_fd); + return ret; + } + + if (!write) + { + ssize_t count = read (as_fd, valp, sizeof (*valp)); + + if (count != sizeof (*valp)) + { + Debug (0, "error %d in read(%zu): %s\n", errno, sizeof (*valp), strerror (errno)); + close (as_fd); + return ret; + } + + ret = 0; + } + + close (as_fd); + return ret; +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_access_reg.c b/src/native/external/libunwind/src/nto/unw_nto_access_reg.c new file mode 100644 index 00000000000000..18a317eb94c938 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_access_reg.c @@ -0,0 +1,202 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" +#include "os-qnx.h" + +#include +#include +#include +#include +#include +#include + + +int unw_nto_access_reg (unw_addr_space_t as, + unw_regnum_t regnum, + unw_word_t *valp, + int write, + void *arg) +{ + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + int ret = -UNW_EUNSPEC; + procfs_greg greg; + int ctl_fd = unw_nto_procfs_open_ctl (uni->pid); + + if (ctl_fd < 0) + { + Debug (0, "error %d opening ctl file: %s", errno, strerror (errno)); + return ret; + } + + int err = devctl (ctl_fd, DCMD_PROC_CURTHREAD, & (uni->tid), sizeof (uni->tid), 0); + + if (err != EOK) + { + Debug (0, "error %d in devctl(DCMD_PROC_CURTHREAD): %s", err, strerror (err)); + close (ctl_fd); + return ret; + } + + if (write) + { + Debug (0, "write not supported\n"); + } + + else + { + err = devctl (ctl_fd, DCMD_PROC_GETGREG, &greg, sizeof (greg), NULL); + + if (err != EOK) + { + Debug (0, "error %d in devctl(DCMD_PROC_GETGREG): %s", err, strerror (err)); + close (ctl_fd); + return ret; + } + + switch (regnum) + { + case UNW_REG_IP: +#if defined(__X86_64__) + *valp = GET_REGIP (&greg.x86_64); +#elif defined(__ARM__) + *valp = GET_REGIP (&greg.arm); +#elif defined(__aarch64__) + *valp = greg.aarch64.gpr[AARCH64_REG_X30]; +#else +# error Unsupported architecture +#endif + break; + + case UNW_REG_SP: +#if defined(__X86_64__) + *valp = GET_REGSP (&greg.x86_64); +#elif defined(__ARM__) + *valp = GET_REGSP (&greg.arm); +#elif defined(__aarch64__) + *valp = GET_REGSP (&greg.aarch64); +#else +# error Unsupported architecture +#endif + break; +#if defined(__aarch64__) + + case UNW_AARCH64_PC: + *valp = GET_REGIP (&greg.aarch64); + break; + + case UNW_AARCH64_X29: + *valp = greg.aarch64.gpr[AARCH64_REG_X29]; + break; +#endif +#if defined(__X86_64__) + + case UNW_X86_64_RAX: + Debug (15, "request for UNW_X86_64_RAX (%d)\n", UNW_X86_64_RAX); + *valp = greg.x86_64.rax; + break; + + case UNW_X86_64_RDX: + Debug (15, "request for UNW_X86_64_RDX (%d)\n", UNW_X86_64_RDX); + *valp = greg.x86_64.rdx; + break; + + case UNW_X86_64_RCX: + Debug (15, "request for UNW_X86_64_RCX (%d)\n", UNW_X86_64_RCX); + *valp = greg.x86_64.rcx; + break; + + case UNW_X86_64_RBX: + Debug (15, "request for UNW_X86_64_RBX (%d)\n", UNW_X86_64_RBX); + *valp = greg.x86_64.rbx; + break; + + case UNW_X86_64_RSI: + Debug (15, "request for UNW_X86_64_RSI (%d)\n", UNW_X86_64_RSI); + *valp = greg.x86_64.rsi; + break; + + case UNW_X86_64_RDI: + Debug (15, "request for UNW_X86_64_RDI (%d)\n", UNW_X86_64_RDI); + *valp = greg.x86_64.rdi; + break; + + case UNW_X86_64_RBP: + Debug (15, "request for UNW_X86_64_RBP (%d)\n", UNW_X86_64_RBP); + *valp = greg.x86_64.rbp; + break; + + case UNW_X86_64_R8: + Debug (15, "request for UNW_X86_64_R8 (%d)\n", UNW_X86_64_R8); + *valp = greg.x86_64.r8; + break; + + case UNW_X86_64_R9: + Debug (15, "request for UNW_X86_64_R9 (%d)\n", UNW_X86_64_R9); + *valp = greg.x86_64.r9; + break; + + case UNW_X86_64_R10: + Debug (15, "request for UNW_X86_64_R10 (%d)\n", UNW_X86_64_R10); + *valp = greg.x86_64.r10; + break; + + case UNW_X86_64_R11: + Debug (15, "request for UNW_X86_64_R11 (%d)\n", UNW_X86_64_R11); + *valp = greg.x86_64.r11; + break; + + case UNW_X86_64_R12: + Debug (15, "request for UNW_X86_64_R12 (%d)\n", UNW_X86_64_R12); + *valp = greg.x86_64.r12; + break; + + case UNW_X86_64_R13: + Debug (15, "request for UNW_X86_64_R13 (%d)\n", UNW_X86_64_R13); + *valp = greg.x86_64.r13; + break; + + case UNW_X86_64_R14: + Debug (15, "request for UNW_X86_64_R14 (%d)\n", UNW_X86_64_R14); + *valp = greg.x86_64.r14; + break; + + case UNW_X86_64_R15: + Debug (15, "request for UNW_X86_64_R15 (%d)\n", UNW_X86_64_R15); + *valp = greg.x86_64.r15; + break; +#endif + + default: + Debug (0, "as=%p, regnum=%d, valp=%p, write=%d uni=%p\n", as, regnum, valp, write, uni); + break; + } + + ret = 0; + } + + close (ctl_fd); + return ret; +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_accessors.c b/src/native/external/libunwind/src/nto/unw_nto_accessors.c new file mode 100644 index 00000000000000..1ccc8215c593ef --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_accessors.c @@ -0,0 +1,44 @@ +/* + * Copyright 2020, 2022-2023 QNX Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "libunwind-nto.h" + + +/** + * Instance of the NTO accessor struct. + */ +unw_accessors_t unw_nto_accessors = +{ + .find_proc_info = unw_nto_find_proc_info, + .put_unwind_info = unw_nto_put_unwind_info, + .get_dyn_info_list_addr = unw_nto_get_dyn_info_list_addr, + .access_mem = unw_nto_access_mem, + .access_reg = unw_nto_access_reg, + .access_fpreg = unw_nto_access_fpreg, + .resume = unw_nto_resume, + .get_proc_name = unw_nto_get_proc_name, + .get_proc_ip_range = unw_nto_get_proc_ip_range, + .get_elf_filename = unw_nto_get_elf_filename, +}; + diff --git a/src/native/external/libunwind/src/nto/unw_nto_create.c b/src/native/external/libunwind/src/nto/unw_nto_create.c new file mode 100644 index 00000000000000..08b6a0469b8efe --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_create.c @@ -0,0 +1,102 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "os-qnx.h" +#include "unw_nto_internal.h" + +#include +#include +#include +#include +#include +#include +#include + + +/** + * Hold the target thread to avoid race conditions. + * @param[in] ctl_fd File descriptor for /proc process ctl file + * @param[in] tid Identifies thread to hold + * + * @returns 0 on error, otherwise on success. + */ +static int _hold_thread (int ctl_fd, pthread_t tid) +{ + procfs_threadctl tctl = + { + .cmd = _NTO_TCTL_ONE_THREAD_HOLD, + .tid = tid + }; + memcpy (tctl.data, & (tid), sizeof (tid)); + int err = devctl (ctl_fd, DCMD_PROC_THREADCTL, &tctl, sizeof (tctl), NULL); + + if (err != EOK) + { + Debug (0, "error %d in devctl(DCMD_PROC_THREADCTL): %s\n", err, strerror (err)); + } + + return err == EOK; +} + + +void *unw_nto_create (pid_t pid, pthread_t tid) +{ + unw_nto_internal_t *uni = calloc (1, sizeof (unw_nto_internal_t)); + if (uni == NULL) + { + return NULL; + } + + mi_init (); + + uni->pid = pid; + uni->tid = tid; + uni->edi.di_cache.format = -1; + uni->edi.di_debug.format = -1; + int ctl_fd = unw_nto_procfs_open_ctl (pid); + + if (ctl_fd < 0) + { + Debug (0, "error %d opening procfs ctl file for pid %d: %s\n", + errno, pid, strerror (errno)); + unw_nto_destroy (uni); + uni = NULL; + return uni; + } + + if (!_hold_thread (ctl_fd, tid)) + { + close (ctl_fd); + unw_nto_destroy (uni); + uni = NULL; + } + + else + { + close (ctl_fd); + } + + return uni; +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_destroy.c b/src/native/external/libunwind/src/nto/unw_nto_destroy.c new file mode 100644 index 00000000000000..407469b6faf9ce --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_destroy.c @@ -0,0 +1,81 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" +#include "os-qnx.h" + +#include +#include +#include +#include +#include +#include + + +/** + * Continues a (held) thread. + * @param[in] ctl_fd File descriptor for /proc process ctl file + * @param[in] tid Identifies thread to continue + * + * @returns 0 on error, otherwise on success. + */ +static void _cont_thread (int ctl_fd, pthread_t tid) +{ + procfs_threadctl tctl = + { + .cmd = _NTO_TCTL_ONE_THREAD_CONT, + .tid = tid + }; + memcpy (tctl.data, & (tid), sizeof (tid)); + int ret = devctl (ctl_fd, DCMD_PROC_THREADCTL, &tctl, sizeof (tctl), NULL); + + if (ret != EOK) + { + Debug (0, "error %d continuing thread %d: %s\n", + ret, tid, strerror (ret)); + } +} + + +/** + * Tears down an NTO unwind context. + */ +void unw_nto_destroy (void *arg) +{ + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + int ctl_fd = unw_nto_procfs_open_ctl (uni->pid); + + if (ctl_fd < 0) + { + Debug (0, "error %d opening procfs ctl file for pid %d: %s\n", + errno, uni->pid, strerror (errno)); + free (uni); + return; + } + + _cont_thread (ctl_fd, uni->tid); + close (ctl_fd); + free (uni); +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_elf.c b/src/native/external/libunwind/src/nto/unw_nto_elf.c new file mode 100644 index 00000000000000..b12641d776ef05 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_elf.c @@ -0,0 +1,33 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * We need to get a separate copy of the ELF-code into libunwind-nto since it + * cannot (and must not) have any ELF dependencies on libunwind. + */ + +#include "libunwind_i.h" /* get ELFCLASS defined */ +#include "../elfxx.c" + diff --git a/src/native/external/libunwind/src/nto/unw_nto_find_proc_info.c b/src/native/external/libunwind/src/nto/unw_nto_find_proc_info.c new file mode 100644 index 00000000000000..a3fd115feda414 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_find_proc_info.c @@ -0,0 +1,100 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Locate info needed to unwind a particular procedure. + * @param[in] as The address space object + * @param[in] ip Address of an instruction inside the procedure. + * @param[out] pi Pointer to where the information should be written. + * @param[in] need_unwind_info Flags is certain fields are mandatory i *pi: + * - format + * - unwind_info_size + * - unwind_info + * @param[in] arg The NTO unwind context. + * + * @returns 0 on normal, successful completion and @c -UNW_ESTOPUNWIND to signal + * the end of the frame-chain. Returns @c -UNW_ENOINFO otherwise indicating an + * error. + * + * This function is part of the public API of the libunwind API library and is a + * callback passed to @c unw_create_addr_space() through the @c unw_accessors_t + * object. + */ +int unw_nto_find_proc_info (unw_addr_space_t as, + unw_word_t ip, + unw_proc_info_t *pi, + int need_unwind_info, + void *arg) +{ + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + int ret = -UNW_ENOINFO; + unsigned long segbase = 0; + unsigned long mapoff = 0; + char path[PATH_MAX]; + invalidate_edi (&uni->edi); + ret = tdep_get_elf_image (&uni->edi.ei, + uni->pid, + ip, + &segbase, + &mapoff, + path, + sizeof (path)); + + if (ret >= 0) + { + if (tdep_find_unwind_table (&uni->edi, as, path, segbase, mapoff, ip) >= 0) + { + if (uni->edi.di_cache.format != -1) + { + ret = tdep_search_unwind_table (as, ip, &uni->edi.di_cache, + pi, need_unwind_info, uni); + } + + if (ret == -UNW_ENOINFO && uni->edi.di_debug.format != -1) + { + ret = tdep_search_unwind_table (as, ip, &uni->edi.di_debug, pi, + need_unwind_info, uni); + } + } + } + + return ret; +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_get_dyn_info_list_addr.c b/src/native/external/libunwind/src/nto/unw_nto_get_dyn_info_list_addr.c new file mode 100644 index 00000000000000..b515441e34d197 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_get_dyn_info_list_addr.c @@ -0,0 +1,38 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" + + +/** + * Get the dynamic unwind info registration list. This implementation isn't + * supporting dynamically-generated code so this function does nothing. + */ +int unw_nto_get_dyn_info_list_addr (unw_addr_space_t as, + unw_word_t *dilap, + void *arg) +{ + return -UNW_ENOINFO; +} + diff --git a/src/native/external/libunwind/src/nto/unw_nto_get_elf_filename.c b/src/native/external/libunwind/src/nto/unw_nto_get_elf_filename.c new file mode 100644 index 00000000000000..1654099f6c356d --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_get_elf_filename.c @@ -0,0 +1,51 @@ +/* + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" + +#include +#include + +int unw_nto_get_elf_filename (unw_addr_space_t as, + unw_word_t ip, + char *buf, + size_t buf_len, + unw_word_t *offp, + void *arg) +{ + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + char path[PATH_MAX] = {0}; + size_t path_len = sizeof (path); + int ret = -UNW_ENOINFO; +#if UNW_ELF_CLASS == UNW_ELFCLASS64 + ret = _Uelf64_get_elf_filename (as, uni->pid, ip, path, path_len, offp); +#elif UNW_ELF_CLASS == UNW_ELFCLASS32 + ret = _Uelf32_get_elf_filename (as, uni->pid, ip, path, path_len, offp); +#else +# error no valid ELF class defined +#endif + + if (ret >= 0) + snprintf (buf, buf_len, "%s", path); + + return ret; +} diff --git a/src/native/external/libunwind/src/nto/unw_nto_get_proc_name.c b/src/native/external/libunwind/src/nto/unw_nto_get_proc_name.c new file mode 100644 index 00000000000000..87a2af07fb42b4 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_get_proc_name.c @@ -0,0 +1,88 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "unw_nto_internal.h" + +#include +#include + + +/** + * Callback to obtain the name of the procedure in the current frame and the + * relative offset of the IP within that procedure. + */ +int unw_nto_get_proc_name (unw_addr_space_t as, + unw_word_t ip, + char *buf, + size_t buf_len, + unw_word_t *offp, + void *arg) +{ + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + char symbol[PATH_MAX] = {0}; + size_t symbol_len = sizeof (symbol); + char path[PATH_MAX] = {0}; + size_t path_len = sizeof (path); + int ret = -UNW_ENOINFO; +#if UNW_ELF_CLASS == UNW_ELFCLASS64 + ret = _Uelf64_get_proc_name (as, uni->pid, ip, symbol, symbol_len, offp); +#elif UNW_ELF_CLASS == UNW_ELFCLASS32 + ret = _Uelf32_get_proc_name (as, uni->pid, ip, symbol, symbol_len, offp); +#else +# error no valid ELF class defined +#endif + + if (ret >= 0) + { + /* snprintf(buf, buf_len, "%s:%s", path, symbol); */ + snprintf (buf, buf_len, "%s", symbol); + } + + else if (path[0] != '\0') + { + snprintf (buf, buf_len, "%s:?????", path); + } + + return ret; +} + +int unw_nto_get_proc_ip_range (unw_addr_space_t as, + unw_word_t ip, + unw_word_t *start, + unw_word_t *end, + void *arg) +{ + unw_nto_internal_t *uni = (unw_nto_internal_t *)arg; + int ret = -UNW_ENOINFO; + +#if UNW_ELF_CLASS == UNW_ELFCLASS64 + ret = _Uelf64_get_proc_ip_range (as, uni->pid, ip, start, end); +#elif UNW_ELF_CLASS == UNW_ELFCLASS32 + ret = _Uelf32_get_proc_ip_range (as, uni->pid, ip, start, end); +#else +# error no valid ELF class defined +#endif + + return ret; +} diff --git a/src/native/external/libunwind/src/nto/unw_nto_internal.h b/src/native/external/libunwind/src/nto/unw_nto_internal.h new file mode 100644 index 00000000000000..ed79ab25200754 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_internal.h @@ -0,0 +1,44 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UNW_NTO_INTERNAL_H +#define UNW_NTO_INTERNAL_H + +#include "libunwind-nto.h" +#include "libunwind_i.h" + + +/** + * Internal unw-nto context. + */ +typedef struct unw_nto_internal +{ + pid_t pid; /* process ID of the thread being unwound */ + pthread_t tid; /* thread ID of the thread being unwound */ + struct elf_dyn_info edi; /* ELF info for current frame */ +} unw_nto_internal_t; + + +#endif /* UNW_NTO_INTERNAL_H */ + diff --git a/src/native/external/libunwind/src/nto/unw_nto_put_unwind_info.c b/src/native/external/libunwind/src/nto/unw_nto_put_unwind_info.c new file mode 100644 index 00000000000000..2c6ec1dfb0d4d8 --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_put_unwind_info.c @@ -0,0 +1,37 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "libunwind-nto.h" +#include "unw_nto_internal.h" + + +/** + * @todo unsupported + */ +void unw_nto_put_unwind_info (unw_addr_space_t as, + unw_proc_info_t *pi, + void *arg) +{ + Debug (2, "as=%p, pi=%p, arg=%p\n", as, pi, arg); +} diff --git a/src/native/external/libunwind/src/nto/unw_nto_resume.c b/src/native/external/libunwind/src/nto/unw_nto_resume.c new file mode 100644 index 00000000000000..b250c3ed565d1f --- /dev/null +++ b/src/native/external/libunwind/src/nto/unw_nto_resume.c @@ -0,0 +1,39 @@ +/* + * Copyright 2020, 2022 Blackberry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "libunwind-nto.h" +#include "unw_nto_internal.h" + + +/** + * @todo unsupported + */ +int unw_nto_resume (unw_addr_space_t as, + unw_cursor_t *reg, + void *arg) +{ + Debug (2, "as=%p, reg=%p, arg=%p\n", as, reg, arg); + return 0; +} + diff --git a/src/native/external/libunwind/src/os-freebsd.c b/src/native/external/libunwind/src/os-freebsd.c index 753e819dfe4f6f..2d7c6bea920eb8 100644 --- a/src/native/external/libunwind/src/os-freebsd.c +++ b/src/native/external/libunwind/src/os-freebsd.c @@ -37,7 +37,7 @@ get_mem(size_t sz) { void *res; - res = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + res = mi_mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (res == MAP_FAILED) return (NULL); return (res); @@ -46,7 +46,7 @@ get_mem(size_t sz) static void free_mem(void *ptr, size_t sz) { - munmap(ptr, sz); + mi_munmap(ptr, sz); } static int @@ -136,8 +136,12 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, if (path) { strncpy(path, kv->kve_path, pathlen); + path[pathlen - 1] = '\0'; } - ret = elf_map_image (ei, kv->kve_path); + if (ei) + ret = elf_map_image (ei, kv->kve_path); + else + ret = strlen (kv->kve_path) >= pathlen ? -UNW_ENOMEM : UNW_ESUCCESS; break; } free_mem(buf, len1); diff --git a/src/native/external/libunwind/src/os-hpux.c b/src/native/external/libunwind/src/os-hpux.c index 48bfb05cf54a00..7b8a2313cf829a 100644 --- a/src/native/external/libunwind/src/os-hpux.c +++ b/src/native/external/libunwind/src/os-hpux.c @@ -38,6 +38,7 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, { struct load_module_desc lmd; const char *path2; + int ret; if (pid != getpid ()) { @@ -63,7 +64,11 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, } Debug(1, "segbase=%lx, mapoff=%lx, path=%s\n", *segbase, *mapoff, path); - return elf_map_image (ei, path); + if (ei) + ret = elf_map_image (ei, path); + else + ret = strlen (path2) >= path ? -UNW_ENOMEM : UNW_ESUCCESS; + return ret; } #ifndef UNW_REMOTE_ONLY diff --git a/src/native/external/libunwind/src/os-linux.c b/src/native/external/libunwind/src/os-linux.c index f61050c82c2a3e..b47abc394b8623 100644 --- a/src/native/external/libunwind/src/os-linux.c +++ b/src/native/external/libunwind/src/os-linux.c @@ -33,20 +33,17 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "libunwind_i.h" #include "os-linux.h" -#define FULL_PATH_BUFF_SZ 1024 - int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, unsigned long *segbase, unsigned long *mapoff, char *path, size_t pathlen) { struct map_iterator mi; - int found = 0, rc; + int found = 0, rc = UNW_ESUCCESS; unsigned long hi; char root[sizeof ("/proc/0123456789/root")], *cp; char *full_path; struct stat st; - char full_path_buff[FULL_PATH_BUFF_SZ]; if (maps_init (&mi, pid) < 0) return -1; @@ -64,6 +61,18 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, return -1; } + // get path only, no need to map elf image + if (!ei && path) + { + strncpy(path, mi.path, pathlen); + path[pathlen - 1] = '\0'; + if (strlen(mi.path) >= pathlen) + rc = -UNW_ENOMEM; + + maps_close (&mi); + return rc; + } + full_path = mi.path; /* Get process root */ @@ -72,34 +81,30 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, assert (cp + 6 < root + sizeof (root)); memcpy (cp, "/root", 6); + size_t _len = strlen (mi.path) + 1; if (!stat(root, &st) && S_ISDIR(st.st_mode)) + _len += strlen (root); + else + root[0] = '\0'; + + full_path = path; + if(!path) + full_path = (char*) malloc (_len); + else if(_len >= pathlen) // passed buffer is too small, fail { - size_t _len = strlen(root) + strlen(mi.path) + 1; - if(_len >= FULL_PATH_BUFF_SZ) - { - full_path = (char*) malloc(_len); - } - else - { - snprintf(full_path_buff, FULL_PATH_BUFF_SZ, "%s%s", root, mi.path); - full_path = &full_path_buff[0]; - } - if (!full_path) - full_path = mi.path; - else - { - strcpy (full_path, root); - strcat (full_path, mi.path); - } + maps_close (&mi); + return -1; } - if (path) - { - strncpy(path, full_path, pathlen); - } + strcpy (full_path, root); + strcat (full_path, mi.path); + + if (stat(full_path, &st) || !S_ISREG(st.st_mode)) + strcpy(full_path, mi.path); + rc = elf_map_image (ei, full_path); - if (full_path && full_path != mi.path && full_path != &full_path_buff[0]) + if (!path) free (full_path); maps_close (&mi); diff --git a/src/native/external/libunwind/src/os-linux.h b/src/native/external/libunwind/src/os-linux.h index 29aab5cf40ae47..fb1d4f50fd2faa 100644 --- a/src/native/external/libunwind/src/os-linux.h +++ b/src/native/external/libunwind/src/os-linux.h @@ -77,9 +77,8 @@ maps_init (struct map_iterator *mi, pid_t pid) { /* Try to allocate a page-sized buffer. */ mi->buf_size = getpagesize (); - cp = mmap (NULL, mi->buf_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (cp == MAP_FAILED) + GET_MEMORY (cp, mi->buf_size); + if (!cp) { close(mi->fd); mi->fd = -1; @@ -120,10 +119,10 @@ scan_hex (char *cp, unsigned long *valp) digit = *cp; if ((digit - '0') <= 9) digit -= '0'; - else if ((digit - 'a') < 6) - digit -= 'a' - 10; else if ((digit - 'A') < 6) digit -= 'A' - 10; + else if ((digit - 'a') < 6) + digit -= 'a' - 10; else break; val = (val << 4) | digit; @@ -306,7 +305,7 @@ maps_close (struct map_iterator *mi) mi->fd = -1; if (mi->buf) { - munmap (mi->buf_end - mi->buf_size, mi->buf_size); + mi_munmap (mi->buf_end - mi->buf_size, mi->buf_size); mi->buf = mi->buf_end = NULL; } } diff --git a/src/native/external/libunwind/src/os-qnx.c b/src/native/external/libunwind/src/os-qnx.c index 4a76c7cda41a5f..f306929e677a27 100644 --- a/src/native/external/libunwind/src/os-qnx.c +++ b/src/native/external/libunwind/src/os-qnx.c @@ -1,6 +1,7 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2013 Garmin International Contributed by Matt Fischer + Copyright (c) 2022-2023 BlackBerry Limited. All rights reserved. This file is part of libunwind. @@ -23,85 +24,308 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "libunwind_i.h" +#include "os-qnx.h" + +#include +#include #include +#include +#include +#include -#include "libunwind_i.h" + +#if __PTR_BITS__ == 32 +typedef Elf32_Ehdr elf_ehdr_t; +typedef Elf32_Phdr elf_phdr_t; +#else +typedef Elf64_Ehdr elf_ehdr_t; +typedef Elf64_Phdr elf_phdr_t; +#endif struct cb_info { - unw_word_t ip; - unsigned long segbase; - unsigned long offset; - const char *path; + unw_word_t ip; + unsigned long segbase; + unsigned long offset; + const char *path; }; -static int callback(const struct dl_phdr_info *info, size_t size, void *data) +static int +phdr_callback(const struct dl_phdr_info *info, size_t size, void *data) { int i; struct cb_info *cbi = (struct cb_info*)data; - for(i=0; idlpi_phnum; i++) { - int segbase = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; - if(cbi->ip >= segbase && cbi->ip < segbase + info->dlpi_phdr[i].p_memsz) + for(i=0; idlpi_phnum; i++) { - cbi->path = info->dlpi_name; - cbi->offset = info->dlpi_phdr[i].p_offset; - cbi->segbase = segbase; - return 1; + unw_word_t segbase = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; + if(cbi->ip >= segbase && cbi->ip < segbase + info->dlpi_phdr[i].p_memsz) + { + cbi->path = info->dlpi_name; + cbi->offset = info->dlpi_phdr[i].p_offset; + cbi->segbase = segbase; + return 1; + } } - } return 0; } -int -tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, - unsigned long *segbase, unsigned long *mapoff, - char *path, size_t pathlen) + +/** + * Gets the number of mapped segments loaded in the target process image. + * + * @param[in] ctl_fd file descriptor for the process control file + * + * @returns the number of mapped segments loaded in the process image. + */ +static int +_get_map_count(int ctl_fd) { - struct cb_info cbi; - int ret = -1; - cbi.ip = ip; - cbi.segbase = 0; - cbi.offset = 0; - cbi.path = NULL; - - /* QNX's support for accessing symbol maps is severely broken. There is - a devctl() call that can be made on a proc node (DCMD_PROC_MAPDEBUG) - which returns information similar to Linux's /proc//maps - node, however the filename that is returned by this call is not an - absolute path, and there is no foolproof way to map the filename - back to the file that it came from. - - Therefore, the normal approach for implementing this function, - which works equally well for both local and remote unwinding, - will not work here. The only type of image lookup which works - reliably is locally, using dl_iterate_phdr(). However, the only - time that this function is required to look up a remote image is for - ptrace support, which doesn't work on QNX anyway. Local unwinding, - which is the main case that makes use of this function, will work - fine with dl_iterate_phdr(). Therefore, in lieu of any better - platform support for remote image lookup, this function has just - been implemented in terms of dl_iterate_phdr(). - */ + int count = 0; + int err = devctl(ctl_fd, DCMD_PROC_MAPINFO, NULL, 0, &count); + if (err != EOK) + { + fprintf(stderr, "error %d in devctl(DCMD_PROC_MAPINFO): %s\n", err, strerror(err)); + return 0; + } - if (pid != getpid()) - { - /* Return an error if an attempt is made to perform remote image lookup */ - return -1; - } + return count; +} - if (dl_iterate_phdr (callback, &cbi) != 0) + +/** + * Read some bytes from the procfs address space file for the target process. + * + * @param[in] as_fd file descriptor of te opened address space file + * @param[in] pos offset within the file to start the read + * @param[in] sz number of bytes to read + * @param[out] buf destination in which to read the bytes + * + * @returns the number of bytes read. On failure to read, a byte count of 0 is + * returned and errno is set appropriately. + */ +static int +_read_procfs_as(int as_fd, + uintptr_t pos, + size_t sz, + void *buf) +{ + if (lseek(as_fd, (off_t)pos, SEEK_SET) == -1) + { + fprintf(stderr, "error %d in lseek(%" PRIxPTR "): %s\n", errno, pos, strerror(errno)); + return 0; + } + + size_t bytes_read = 0; + for (size_t readn = sz; readn > 0; ) + { + int ret = read(as_fd, buf + bytes_read, readn); + if (ret <= 0) + { + if (errno == EINTR || errno == EAGAIN) + { + continue; + } + else if (ret == 0) + { + errno = EFAULT; + } + return 0; + } + bytes_read += ret; + readn -= ret; + } + + return sz; +} + + +/** + * Indicate of a chunk of memory is a valid ELF header. + * + * @param[in] e_ident A (putative) ELF header + * + * @return true if it's a valid ELF header, false otherwise. + */ +static bool +_is_elf_header(unsigned char e_ident[EI_NIDENT]) +{ + return e_ident[EI_MAG0] == ELFMAG0 + && e_ident[EI_MAG1] == ELFMAG1 + && e_ident[EI_MAG2] == ELFMAG2 + && e_ident[EI_MAG3] == ELFMAG3; +} + + +static int +_get_remote_elf_image(struct elf_image *ei, + pid_t pid, + unw_word_t ip, + unsigned long *segbase, + unsigned long *mapoff, + char *path, + size_t pathlen) +{ + int ret = -UNW_ENOINFO; + + union { - if (path) + procfs_debuginfo i; + char path[PATH_MAX]; + } debug_info; + + int ctl_fd = unw_nto_procfs_open_ctl(pid); + if (ctl_fd < 0) + { + fprintf(stderr, "error %d opening procfs ctl file for pid %d: %s\n", + errno, pid, strerror(errno)); + return ret; + } + + int as_fd = unw_nto_procfs_open_as(pid); + if (as_fd < 0) + { + fprintf(stderr, "error %d opening procfs as file for pid %d: %s\n", + errno, pid, strerror(errno)); + close(ctl_fd); + return -UNW_ENOINFO; + } + + int map_count = _get_map_count(ctl_fd); + size_t maps_size = sizeof(procfs_mapinfo) * map_count; + procfs_mapinfo *maps = malloc(maps_size); + if (maps == NULL) + { + fprintf(stderr, "error %d in malloc(%zu): %s", errno, maps_size, strerror(errno)); + close (as_fd); + close (ctl_fd); + return -UNW_ENOINFO; + } + + int nmaps = 0; + ret = devctl(ctl_fd, DCMD_PROC_MAPINFO, maps, maps_size, &nmaps); + if (ret != EOK) { - strncpy (path, cbi.path, pathlen); + fprintf(stderr, "error %d in devctl(DCMD_PROC_MAPINFO): %s", ret, strerror(ret)); + free(maps); + close (as_fd); + close (ctl_fd); + return -UNW_ENOINFO; } - *mapoff = cbi.offset; - *segbase = cbi.segbase; + int i = 0; + for (; i < nmaps; ++i) + { + if (maps[i].flags & (MAP_ELF | PROT_EXEC)) + { + uintptr_t vaddr = maps[i].vaddr; + + elf_ehdr_t elf_ehdr; + ret = _read_procfs_as(as_fd, vaddr, sizeof(elf_ehdr), &elf_ehdr); + if (ret != sizeof(elf_ehdr)) + { + continue; + } + + /* Skip region if it's not an ELF segment. */ + if (!_is_elf_header(elf_ehdr.e_ident)) + { + continue; + } + size_t size = elf_ehdr.e_ehsize; + + debug_info.i.vaddr = vaddr; + debug_info.i.path[0]=0; + ret = devctl(ctl_fd, DCMD_PROC_MAPDEBUG, &debug_info, sizeof(debug_info), 0); + if (ret != EOK) + { + fprintf(stderr, "error %d in devctl(DCMD_PROC_MAPDEBUG): %s", ret, strerror(ret)); + continue; + } + uintptr_t reloc = debug_info.i.vaddr; + + elf_phdr_t elf_phdr; + uintptr_t phdr_offset = vaddr + elf_ehdr.e_phoff; + for (int i = 0; i < elf_ehdr.e_phnum; ++i, phdr_offset+=elf_ehdr.e_phentsize) + { + ret = _read_procfs_as(as_fd, phdr_offset, sizeof(elf_phdr_t), &elf_phdr); + if (ret == -1) + { + continue; + } + if (elf_phdr.p_type == PT_LOAD && !(elf_phdr.p_flags&PF_W)) + { + size += elf_phdr.p_memsz; + } + } + + /* Skip segment if the IP is not contained within it. */ + if ((ip < vaddr) || (ip >= (vaddr + size))) + { + continue; + } + + *segbase = vaddr; + *mapoff = reloc; + if (path) + { + strncpy(path, debug_info.i.path, pathlen); + path[pathlen - 1] = '\0'; + } + + if (ei) + ret = elf_map_image(ei, path); + else + ret = strlen (debug_info.i.path) >= pathlen ? -UNW_ENOMEM : UNW_ESUCCESS; - ret = elf_map_image (ei, cbi.path); - } + break; + } + } + + free(maps); + close(as_fd); + close(ctl_fd); + return i == nmaps ? -UNW_ENOINFO : ret; +} + + +int +tdep_get_elf_image(struct elf_image *ei, pid_t pid, unw_word_t ip, + unsigned long *segbase, unsigned long *mapoff, + char *path, size_t pathlen) +{ + int ret = -UNW_ENOINFO; + if (pid != getpid()) + { + ret = _get_remote_elf_image(ei, pid, ip, segbase, mapoff, path, pathlen); + return ret; + } + else + { + struct cb_info cbi; + cbi.ip = ip; + cbi.segbase = 0; + cbi.offset = 0; + cbi.path = NULL; + + if (dl_iterate_phdr (phdr_callback, &cbi) != 0) + { + if (path) + { + strncpy (path, cbi.path, pathlen); + path[pathlen - 1] = '\0'; + } + + *mapoff = cbi.offset; + *segbase = cbi.segbase; + + if (ei) + ret = elf_map_image (ei, cbi.path); + else + ret = strlen (cbi.path) >= pathlen ? -UNW_ENOMEM : UNW_ESUCCESS; + } + } return ret; } diff --git a/src/native/external/libunwind/src/os-qnx.h b/src/native/external/libunwind/src/os-qnx.h new file mode 100644 index 00000000000000..daf0b39f142baf --- /dev/null +++ b/src/native/external/libunwind/src/os-qnx.h @@ -0,0 +1,118 @@ +/* + * Copyright 2022-2023 BlackBerry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef OS_QNX_H +#define OS_QNX_H + +#include +#include +#include +#include +#include +#include + + +/** + * Return a buffer contaiing the procfs file name, or NULL on failure. + * @param[in] pid Identify the target process + * @param[in] ftype The type of procfs file to name. + * + * @returns a pointer to a newly-allocated string containing the name of the + * procfs file of type @p ftype for process @p pid. The returned buffer needs to + * be free()d. Returns NULL on error and errno is set appropriately. + */ +static inline char * +get_proc_filename(pid_t pid, char const *ftype) +{ + char *filename = NULL; + int pathlen = snprintf(NULL, 0, "/proc/%d/%s", pid, ftype); + int saved_errno = errno; + if (pathlen > 0) + { + ++pathlen; + filename = malloc(pathlen); + saved_errno = errno; + if (filename != NULL) + { + int len = snprintf(filename, pathlen, "/proc/%d/%s", pid, ftype); + saved_errno = errno; + if (len < 0) + { + free(filename); + filename = NULL; + } + } + } + errno = saved_errno; + return filename; +} + +/** + * Opens the procfs address space file for a process. + * @param[in] pid Identify the target process. + * + * @returns a valid file descriptor for the opened procfs address space file or + * -1 on failure (and errno is set appropriately). + */ +static inline int +unw_nto_procfs_open_as(pid_t pid) +{ + int as_fd = -1; + char *as_filename = get_proc_filename(pid, "as"); + if (as_filename != NULL) + { + as_fd = open(as_filename, O_CLOEXEC | O_RDONLY); + int saved_errno = errno; + free(as_filename); + errno = saved_errno; + } + return as_fd; +} + + +/** + * Opens the procfs control file for a process. + * @param[in] pid Identify the target process. + * + * @returns a valid file descriptor for the opened procfs control file or + * -1 on failure (and errno is set appropriately). + */ +static inline int +unw_nto_procfs_open_ctl(pid_t pid) +{ + int ctl_fd = -1; + char *ctl_filename = get_proc_filename(pid, "ctl"); + if (ctl_filename != NULL) + { + ctl_fd = open(ctl_filename, O_CLOEXEC | O_RDWR); + int saved_errno = errno; + free(ctl_filename); + errno = saved_errno; + } + return ctl_fd; +} + + + +#endif /* OS_QNX_H */ diff --git a/src/native/external/libunwind/src/os-solaris.c b/src/native/external/libunwind/src/os-solaris.c index f0210db1f2b72d..032bacf6711fd0 100644 --- a/src/native/external/libunwind/src/os-solaris.c +++ b/src/native/external/libunwind/src/os-solaris.c @@ -35,7 +35,7 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, char *path, size_t pathlen) { struct map_iterator mi; - int found = 0, rc; + int found = 0, rc = UNW_ESUCCESS; unsigned long hi; if (maps_init (&mi, pid) < 0) @@ -53,11 +53,18 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, maps_close (&mi); return -1; } + if (path) { strncpy(path, mi.path, pathlen); + path[pathlen - 1] = '\0'; } - rc = elf_map_image (ei, mi.path); + + if (ei) + rc = elf_map_image (ei, mi.path); + else + rc = strlen(mi.path) >= pathlen ? -UNW_ENOMEM : UNW_ESUCCESS:; + maps_close (&mi); return rc; } diff --git a/src/native/external/libunwind/src/ppc32/Ginit.c b/src/native/external/libunwind/src/ppc32/Ginit.c index d978a775265328..9444cbb867be72 100644 --- a/src/native/external/libunwind/src/ppc32/Ginit.c +++ b/src/native/external/libunwind/src/ppc32/Ginit.c @@ -127,14 +127,20 @@ static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { + if (unlikely (as->validate) && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) + { + Debug (16, "mem[%#010lx] invalid\n", (long)addr); + return -1; + } + if (write) { Debug (12, "mem[%lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "mem[%lx] -> %lx\n", addr, *val); } return 0; @@ -157,12 +163,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %lx\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %lx\n", unw_regname (reg), *val); } return 0; @@ -212,10 +218,23 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf32_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void ppc32_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -225,6 +244,7 @@ ppc32_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = ppc32_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/ppc32/Gstep.c b/src/native/external/libunwind/src/ppc32/Gstep.c index 478d3a6c1b89f5..0ba07b2cda85cf 100644 --- a/src/native/external/libunwind/src/ppc32/Gstep.c +++ b/src/native/external/libunwind/src/ppc32/Gstep.c @@ -57,6 +57,7 @@ unw_step (unw_cursor_t * cursor) unw_word_t back_chain_offset, lr_save_offset; struct dwarf_loc back_chain_loc, lr_save_loc, sp_loc, ip_loc; int ret; + int validate = c->dwarf.as->validate; Debug (1, "(cursor=%p, ip=0x%016lx)\n", c, (unsigned long) c->dwarf.ip); @@ -69,7 +70,9 @@ unw_step (unw_cursor_t * cursor) /* Try DWARF-based unwinding... */ + c->dwarf.as->validate = 1; ret = dwarf_step (&c->dwarf); + c->dwarf.as->validate = validate; if (ret < 0 && ret != -UNW_ENOINFO) { diff --git a/src/native/external/libunwind/src/ppc32/ucontext_i.h b/src/native/external/libunwind/src/ppc32/ucontext_i.h index 64f8ed878ad8e7..ee93c697941c31 100644 --- a/src/native/external/libunwind/src/ppc32/ucontext_i.h +++ b/src/native/external/libunwind/src/ppc32/ucontext_i.h @@ -44,8 +44,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //#define MQ_IDX 36 #define LINK_IDX 36 -#define _UC_MCONTEXT_GPR(x) ( (void *)&dmy_ctxt.uc_mcontext.gregs[x] - (void *)&dmy_ctxt) ) -#define _UC_MCONTEXT_FPR(x) ( ((void *)&dmy_ctxt.uc_mcontext.fpregs[x] - (void *)&dmy_ctxt) ) +#define _UC_MCONTEXT_GPR(x) ( ((void *)&dmy_ctxt.uc_mcontext.uc_regs->gregs[x] - (void *)&dmy_ctxt) ) +#define _UC_MCONTEXT_FPR(x) ( ((void *)&dmy_ctxt.uc_mcontext.uc_regs->fpregs.fpregs[x] - (void *)&dmy_ctxt) ) /* These are dummy structures used only for obtaining the offsets of the various structure members. */ diff --git a/src/native/external/libunwind/src/ppc64/Ginit.c b/src/native/external/libunwind/src/ppc64/Ginit.c index b9683ae2dd6519..8c54bab8a972fa 100644 --- a/src/native/external/libunwind/src/ppc64/Ginit.c +++ b/src/native/external/libunwind/src/ppc64/Ginit.c @@ -135,14 +135,20 @@ static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) { + if (unlikely (as->validate) && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) + { + Debug (16, "mem[%#010lx] invalid\n", (long)addr); + return -1; + } + if (write) { Debug (12, "mem[%lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "mem[%lx] -> %lx\n", addr, *val); } return 0; @@ -166,12 +172,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %lx\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %lx\n", unw_regname (reg), *val); } return 0; @@ -223,6 +229,14 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf64_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void ppc64_local_addr_space_init (void) { @@ -232,6 +246,11 @@ ppc64_local_addr_space_init (void) local_addr_space.abi = UNW_PPC64_ABI_ELFv2; #else local_addr_space.abi = UNW_PPC64_ABI_ELFv1; +#endif +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif #endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; @@ -242,6 +261,7 @@ ppc64_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = ppc64_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/ppc64/Gstep.c b/src/native/external/libunwind/src/ppc64/Gstep.c index 27c8bebc644765..ac6de903c975ac 100644 --- a/src/native/external/libunwind/src/ppc64/Gstep.c +++ b/src/native/external/libunwind/src/ppc64/Gstep.c @@ -50,6 +50,126 @@ typedef struct /* many more fields here, but they are unused by this code */ } stack_frame_t; +//! Recognise PLT entries +/*! For example: + 0x000000001000d1f0 <+0>: 18 00 41 f8 std r2,24(r1) + 0x000000001000d1f4 <+4>: 30 87 82 e9 ld r12,-30928(r2) + 0x000000001000d1f8 <+8>: a6 03 89 7d mtctr r12 + 0x000000001000d1fc <+12>: 20 04 80 4e bctr + + \note The current implementation only supports little endian modes. +*/ +static int +is_plt_entry (struct dwarf_cursor *c) +{ + unw_word_t w0, w1; + unw_accessors_t *a; + + if (c->as->big_endian) + { + return 0; + } + + /* + A PLT (Procedure Linkage Table) is used by the dynamic linker to map the + relative address of a position independent function call onto the real + address of the function. If we attempt to unwind from any instruction + inside the PLT, and the PLT is missing DWARF unwind information, then + conventional unwinding will fail because although the function has been + "called" we have not yet entered the prologue and set-up the stack frame. + + This code looks to see if the instruction is anywhere within a "recognised" + PLT entry (note that the IP could be anywhere within the PLT, so we have to + examine nearby instructions). + */ + + struct instruction_entry + { + uint32_t pattern; + uint32_t mask; + } instructions[4] = + { + // ppc64le + {0xf8410018,0xffffffff}, + {0xe9820000,0xffff0000}, + {0x7d8903a6,0xffffffff}, + {0x4e800420,0xffffffff}, + }; + + a = unw_get_accessors (c->as); + + if( (*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg) < 0 ) + { + return 0; + } + + /* + NB: the following code is endian sensitive! + + The current implementation is for little-endian modes, big-endian modes + will see the first instruction in the high bits of w0, and the second + instruction in the low bits of w0. Some tweaks will be needed to read from + the correct part of the word to support big endian modes. + */ + if(( w0 & instructions[0].mask) == instructions[0].pattern && + ((w0>>32) & instructions[1].mask) == instructions[1].pattern) + { + if( (*a->access_mem) (c->as, c->ip+8, &w1, 0, c->as_arg) >= 0 && + ( w1 & instructions[2].mask) == instructions[2].pattern && + ((w1>>32) & instructions[3].mask) == instructions[3].pattern ) + { + return 1; + } + else + { + return 0; + } + } + else if(( w0 & instructions[2].mask) == instructions[2].pattern && + ((w0>>32) & instructions[3].mask) == instructions[3].pattern) + { + w1 = w0; + if( (*a->access_mem) (c->as, c->ip-8, &w0, 0, c->as_arg) >= 0 && + ( w0 & instructions[0].mask) == instructions[0].pattern && + ((w0>>32) & instructions[1].mask) == instructions[1].pattern ) + { + return 1; + } + else + { + return 0; + } + } + else if(( w0 & instructions[1].mask) == instructions[1].pattern && + ((w0>>32) & instructions[2].mask) == instructions[2].pattern) + { + if( (*a->access_mem) (c->as, c->ip-4, &w0, 0, c->as_arg) < 0 || + (*a->access_mem) (c->as, c->ip+4, &w1, 0, c->as_arg) < 0 ) + { + return 0; + } + } + else if( (w0 & instructions[3].mask) == instructions[3].pattern ) + { + if( (*a->access_mem) (c->as, c->ip-12, &w0, 0, c->as_arg) < 0 || + (*a->access_mem) (c->as, c->ip-4, &w1, 0, c->as_arg) < 0 ) + { + return 0; + } + } + + if(( w0 & instructions[0].mask) == instructions[0].pattern && + ((w0>>32) & instructions[1].mask) == instructions[1].pattern && + ( w1 & instructions[2].mask) == instructions[2].pattern && + ((w1>>32) & instructions[3].mask) == instructions[3].pattern ) + { + return 1; + } + else + { + return 0; + } +} int unw_step (unw_cursor_t * cursor) @@ -59,12 +179,15 @@ unw_step (unw_cursor_t * cursor) unw_word_t back_chain_offset, lr_save_offset, v_regs_ptr; struct dwarf_loc back_chain_loc, lr_save_loc, sp_loc, ip_loc, v_regs_loc; int ret, i; + int validate = c->dwarf.as->validate; Debug (1, "(cursor=%p, ip=0x%016lx)\n", c, (unsigned long) c->dwarf.ip); /* Try DWARF-based unwinding... */ + c->dwarf.as->validate = 1; ret = dwarf_step (&c->dwarf); + c->dwarf.as->validate = validate; if (ret < 0 && ret != -UNW_ENOINFO) { @@ -76,49 +199,97 @@ unw_step (unw_cursor_t * cursor) { if (likely (unw_is_signal_frame (cursor) <= 0)) { - /* DWARF unwinding failed. As of 09/26/2006, gcc in 64-bit mode - produces the mandatory level of traceback record in the code, but - I get the impression that this is transitory, that eventually gcc - will not produce any traceback records at all. So, for now, we - won't bother to try to find and use these records. - - We can, however, attempt to unwind the frame by using the callback - chain. This is very crude, however, and won't be able to unwind - any registers besides the IP, SP, and LR . */ - - back_chain_offset = ((void *) &dummy.back_chain - (void *) &dummy); - lr_save_offset = ((void *) &dummy.lr_save - (void *) &dummy); - - back_chain_loc = DWARF_LOC (c->dwarf.cfa + back_chain_offset, 0); - - if ((ret = - dwarf_get (&c->dwarf, back_chain_loc, &c->dwarf.cfa)) < 0) + /* DWARF failed. */ + if (is_plt_entry (&c->dwarf)) { - Debug (2, - "Unable to retrieve CFA from back chain in stack frame - %d\n", - ret); - return ret; + Debug (2, "found plt entry\n"); + + /* + Fallback mode that uses the link register. This is important + for cases where a function without unwind information has been + called, but not yet set-up its stack frame (basically PLT calls). + + In this case we can't trust c->dwarf.cfa (the stack frame) + because it will currently point to the caller's stack frame - + but we can use the current value of the link register to get + back to the caller. We then have to hope that libunwind manages + to resume unwinding properly from the caller IP. + */ + c->dwarf.loc[UNW_PPC64_NIP] = c->dwarf.loc[UNW_PPC64_LR]; + c->dwarf.loc[UNW_PPC64_LR] = DWARF_NULL_LOC; + if (!DWARF_IS_NULL_LOC (c->dwarf.loc[UNW_PPC64_NIP])) + { + ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_PPC64_NIP], &c->dwarf.ip); + if (ret < 0) + { + Debug (2, "failed to get pc from link register: %d\n", ret); + return ret; + } + Debug (2, "link register = 0x%016lx\n", c->dwarf.ip); + ret = 1; + } + else + { + Debug (2, "link register was not saved\n"); + c->dwarf.ip = 0; + } } - if (c->dwarf.cfa == 0) - /* Unless the cursor or stack is corrupt or uninitialized we've most - likely hit the top of the stack */ - return 0; - - lr_save_loc = DWARF_LOC (c->dwarf.cfa + lr_save_offset, 0); - - if ((ret = dwarf_get (&c->dwarf, lr_save_loc, &c->dwarf.ip)) < 0) + else { - Debug (2, - "Unable to retrieve IP from lr save in stack frame - %d\n", - ret); - return ret; + /* + Fallback mode that uses a conventional stack unwinding. This is + important for functions without proper DWARF unwind information, + in particular without this fallback the clone() function will not + unwind properly. + + We do the unwind by first looking for the caller stack pointer + saved in the current stack frame. This will point to the caller's + 'linkage area'. If the caller stack pointer is null, we assume we + have reached the top of the stack. + + The address 24(SP) (where SP is the caller stack pointer) contains + the saved 'link register', the link register is effectively the + return address for the called function - so we can use the link + register to get an IP inside the calling function. + + Note: there is no requirement for leaf functions to save the + stack pointer or link register, I'm not entirely sure why + libunwind doesn't handle this case. + */ + Debug (2, "fallback\n"); + + back_chain_offset = ((void *) &dummy.back_chain - (void *) &dummy); + lr_save_offset = ((void *) &dummy.lr_save - (void *) &dummy); + + back_chain_loc = DWARF_LOC (c->dwarf.cfa + back_chain_offset, 0); + + if ((ret = + dwarf_get (&c->dwarf, back_chain_loc, &c->dwarf.cfa)) < 0) + { + Debug (2, + "Unable to retrieve CFA from back chain in stack frame - %d\n", + ret); + return ret; + } + if (c->dwarf.cfa == 0) + /* Unless the cursor or stack is corrupt or uninitialized we've most + likely hit the top of the stack */ + return 0; + + lr_save_loc = DWARF_LOC (c->dwarf.cfa + lr_save_offset, 0); + + if ((ret = dwarf_get (&c->dwarf, lr_save_loc, &c->dwarf.ip)) < 0) + { + Debug (2, + "Unable to retrieve IP from lr save in stack frame - %d\n", + ret); + return ret; + } + ret = 1; + /* Mark all registers unsaved */ + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + c->dwarf.loc[i] = DWARF_NULL_LOC; } - - /* Mark all registers unsaved */ - for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) - c->dwarf.loc[i] = DWARF_NULL_LOC; - - ret = 1; } else { diff --git a/src/native/external/libunwind/src/ptrace/_UPT_access_fpreg.c b/src/native/external/libunwind/src/ptrace/_UPT_access_fpreg.c index 3b993ed15fcdeb..7d8eee83d8ea5e 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_access_fpreg.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_access_fpreg.c @@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if HAVE_DECL_PTRACE_POKEUSER || defined(HAVE_TTRACE) int -_UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, +_UPT_access_fpreg (unw_addr_space_t as UNUSED, unw_regnum_t reg, unw_fpreg_t *val, int write, void *arg) { unw_word_t *wp = (unw_word_t *) val; @@ -107,7 +107,11 @@ _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, #elif defined(__i386__) memcpy(&fpreg.fpr_acc[reg], val, sizeof(unw_fpreg_t)); #elif defined(__arm__) +# if __FreeBSD_version >= 1400079 + memcpy(&fpreg.fpr_r[reg], val, sizeof(unw_fpreg_t)); +# else memcpy(&fpreg.fpr[reg], val, sizeof(unw_fpreg_t)); +# endif #elif defined(__aarch64__) memcpy(&fpreg.fp_q[reg], val, sizeof(unw_fpreg_t)); #elif defined(__powerpc__) @@ -123,7 +127,11 @@ _UPT_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, #elif defined(__i386__) memcpy(val, &fpreg.fpr_acc[reg], sizeof(unw_fpreg_t)); #elif defined(__arm__) - memcpy(val, &fpreg.fpr[reg], sizeof(unw_fpreg_t)); +# if __FreeBSD_version >= 1400079 + memcpy(&fpreg.fpr_r[reg], val, sizeof(unw_fpreg_t)); +# else + memcpy(&fpreg.fpr[reg], val, sizeof(unw_fpreg_t)); +# endif #elif defined(__aarch64__) memcpy(val, &fpreg.fp_q[reg], sizeof(unw_fpreg_t)); #elif defined(__powerpc__) diff --git a/src/native/external/libunwind/src/ptrace/_UPT_access_mem.c b/src/native/external/libunwind/src/ptrace/_UPT_access_mem.c index 5d6511c56c6c25..5892982cd7d7bb 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_access_mem.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_access_mem.c @@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if HAVE_DECL_PTRACE_POKEDATA || defined(HAVE_TTRACE) int -_UPT_access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, +_UPT_access_mem (unw_addr_space_t as UNUSED, unw_word_t addr, unw_word_t *val, int write, void *arg) { struct UPT_info *ui = arg; diff --git a/src/native/external/libunwind/src/ptrace/_UPT_access_reg.c b/src/native/external/libunwind/src/ptrace/_UPT_access_reg.c index 033d3aae7f4eaa..316ad19b8abaca 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_access_reg.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_access_reg.c @@ -34,10 +34,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # include "tdep-ia64/rse.h" #endif -#if HAVE_DECL_PTRACE_SETREGSET +#if HAVE_DECL_PTRACE_SETREGSET && (defined(__linux__) && !defined(UNW_TARGET_X86)) #include int -_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, +_UPT_access_reg (unw_addr_space_t as UNUSED, unw_regnum_t reg, unw_word_t *val, int write, void *arg) { struct UPT_info *ui = arg; @@ -77,9 +77,9 @@ _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno)); return -UNW_EBADREG; } -#elif HAVE_DECL_PTRACE_POKEUSER || defined(HAVE_TTRACE) +#elif (HAVE_DECL_PTRACE_POKEUSER || defined(HAVE_TTRACE)) && (defined(__linux__) && !defined(UNW_TARGET_X86)) int -_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, +_UPT_access_reg (UNUSED unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, void *arg) { struct UPT_info *ui = arg; @@ -321,10 +321,49 @@ _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno)); return -UNW_EBADREG; } -#elif HAVE_DECL_PT_GETREGS +#elif defined(HAVE_DECL_PT_GETREGS) && defined(__linux__) +# include + int _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, void *arg) +{ + struct UPT_info *ui = arg; + pid_t pid = ui->pid; + struct user_regs_struct regs; + char *r; + +#if UNW_DEBUG + Debug(16, "using getregs: reg: %s [%u], val: %lx, write: %u\n", unw_regname(reg), (unsigned) reg, (long) val, write); + + if (write) + Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val); +#endif + if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset)) + { + errno = EINVAL; + goto badreg; + } + r = (char *)®s + _UPT_reg_offset[reg]; + if (ptrace(PT_GETREGS, pid, NULL, ®s) == -1) + goto badreg; + if (write) { + memcpy(r, val, sizeof(unw_word_t)); + if (ptrace(PT_SETREGS, pid, NULL, ®s) == -1) + goto badreg; + } else + memcpy(val, r, sizeof(unw_word_t)); + return 0; + + badreg: + Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno)); + return -UNW_EBADREG; +} + +#elif defined(HAVE_DECL_PT_GETREGS) && defined(__FreeBSD__) +int +_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, + int write, void *arg) { struct UPT_info *ui = arg; pid_t pid = ui->pid; @@ -337,6 +376,7 @@ _UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, if (write) Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val); #endif + if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset)) { errno = EINVAL; diff --git a/src/native/external/libunwind/src/ptrace/_UPT_accessors.c b/src/native/external/libunwind/src/ptrace/_UPT_accessors.c index 4724360bb99b9c..364e70a765a7d7 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_accessors.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_accessors.c @@ -34,5 +34,6 @@ unw_accessors_t _UPT_accessors = .access_reg = _UPT_access_reg, .access_fpreg = _UPT_access_fpreg, .resume = _UPT_resume, - .get_proc_name = _UPT_get_proc_name + .get_proc_name = _UPT_get_proc_name, + .get_elf_filename = _UPT_get_elf_filename }; diff --git a/src/native/external/libunwind/src/ptrace/_UPT_create.c b/src/native/external/libunwind/src/ptrace/_UPT_create.c index dd59e974a7eafc..1536c27e9e853d 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_create.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_create.c @@ -32,6 +32,8 @@ _UPT_create (pid_t pid) { struct UPT_info *ui = malloc (sizeof (struct UPT_info)); + mi_init (); + if (!ui) return NULL; diff --git a/src/native/external/libunwind/src/ptrace/_UPT_get_dyn_info_list_addr.c b/src/native/external/libunwind/src/ptrace/_UPT_get_dyn_info_list_addr.c index a71f80d3dbd9d5..3156acfdaf115e 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_get_dyn_info_list_addr.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_get_dyn_info_list_addr.c @@ -77,8 +77,10 @@ get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg, DWARF2 unwind info. */ static inline int -get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg, - int *countp) +get_list_addr (unw_addr_space_t as UNUSED, + unw_word_t *dil_addr UNUSED, + void *arg UNUSED, + int *countp) { # warning Implement get_list_addr(), please. *countp = 0; diff --git a/src/native/external/libunwind/src/ptrace/_UPT_get_elf_filename.c b/src/native/external/libunwind/src/ptrace/_UPT_get_elf_filename.c new file mode 100644 index 00000000000000..401de3d18145c7 --- /dev/null +++ b/src/native/external/libunwind/src/ptrace/_UPT_get_elf_filename.c @@ -0,0 +1,38 @@ +/* + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "_UPT_internal.h" + +int +_UPT_get_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, void *arg) +{ + struct UPT_info *ui = arg; + +#if UNW_ELF_CLASS == UNW_ELFCLASS64 + return _Uelf64_get_elf_filename (as, ui->pid, ip, buf, buf_len, offp); +#elif UNW_ELF_CLASS == UNW_ELFCLASS32 + return _Uelf32_get_elf_filename (as, ui->pid, ip, buf, buf_len, offp); +#else + return -UNW_ENOINFO; +#endif +} diff --git a/src/native/external/libunwind/src/ptrace/_UPT_put_unwind_info.c b/src/native/external/libunwind/src/ptrace/_UPT_put_unwind_info.c index d4b84631476b6b..54989a769a70d3 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_put_unwind_info.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_put_unwind_info.c @@ -26,7 +26,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UPT_internal.h" void -_UPT_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg) +_UPT_put_unwind_info (unw_addr_space_t as UNUSED, unw_proc_info_t *pi, void *arg UNUSED) { if (!pi->unwind_info) return; diff --git a/src/native/external/libunwind/src/ptrace/_UPT_reg_offset.c b/src/native/external/libunwind/src/ptrace/_UPT_reg_offset.c index ea13e6de88cf81..02e0ec6d893662 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_reg_offset.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_reg_offset.c @@ -623,64 +623,6 @@ const int _UPT_reg_offset[UNW_REG_LAST + 1] = [UNW_AARCH64_SP] = 0xf8, [UNW_AARCH64_PC] = 0x100, [UNW_AARCH64_PSTATE] = 0x108 -#elif defined(UNW_TARGET_TILEGX) - [UNW_TILEGX_R0] = 0x00, - [UNW_TILEGX_R1] = 0x08, - [UNW_TILEGX_R2] = 0x10, - [UNW_TILEGX_R3] = 0x08, - [UNW_TILEGX_R4] = 0x20, - [UNW_TILEGX_R5] = 0x28, - [UNW_TILEGX_R6] = 0x30, - [UNW_TILEGX_R7] = 0x38, - [UNW_TILEGX_R8] = 0x40, - [UNW_TILEGX_R9] = 0x48, - [UNW_TILEGX_R10] = 0x50, - [UNW_TILEGX_R11] = 0x58, - [UNW_TILEGX_R12] = 0x60, - [UNW_TILEGX_R13] = 0x68, - [UNW_TILEGX_R14] = 0x70, - [UNW_TILEGX_R15] = 0x78, - [UNW_TILEGX_R16] = 0x80, - [UNW_TILEGX_R17] = 0x88, - [UNW_TILEGX_R18] = 0x90, - [UNW_TILEGX_R19] = 0x98, - [UNW_TILEGX_R20] = 0xa0, - [UNW_TILEGX_R21] = 0xa8, - [UNW_TILEGX_R22] = 0xb0, - [UNW_TILEGX_R23] = 0xb8, - [UNW_TILEGX_R24] = 0xc0, - [UNW_TILEGX_R25] = 0xc8, - [UNW_TILEGX_R26] = 0xd0, - [UNW_TILEGX_R27] = 0xd8, - [UNW_TILEGX_R28] = 0xe0, - [UNW_TILEGX_R29] = 0xe8, - [UNW_TILEGX_R30] = 0xf0, - [UNW_TILEGX_R31] = 0xf8, - [UNW_TILEGX_R32] = 0x100, - [UNW_TILEGX_R33] = 0x108, - [UNW_TILEGX_R34] = 0x110, - [UNW_TILEGX_R35] = 0x118, - [UNW_TILEGX_R36] = 0x120, - [UNW_TILEGX_R37] = 0x128, - [UNW_TILEGX_R38] = 0x130, - [UNW_TILEGX_R39] = 0x138, - [UNW_TILEGX_R40] = 0x140, - [UNW_TILEGX_R41] = 0x148, - [UNW_TILEGX_R42] = 0x150, - [UNW_TILEGX_R43] = 0x158, - [UNW_TILEGX_R44] = 0x160, - [UNW_TILEGX_R45] = 0x168, - [UNW_TILEGX_R46] = 0x170, - [UNW_TILEGX_R47] = 0x178, - [UNW_TILEGX_R48] = 0x180, - [UNW_TILEGX_R49] = 0x188, - [UNW_TILEGX_R50] = 0x190, - [UNW_TILEGX_R51] = 0x198, - [UNW_TILEGX_R52] = 0x1a0, - [UNW_TILEGX_R53] = 0x1a8, - [UNW_TILEGX_R54] = 0x1b0, - [UNW_TILEGX_R55] = 0x1b8, - [UNW_TILEGX_PC] = 0x1a0 #elif defined(UNW_TARGET_S390X) [UNW_S390X_R0] = 0x10, [UNW_S390X_R1] = 0x18, diff --git a/src/native/external/libunwind/src/ptrace/_UPT_resume.c b/src/native/external/libunwind/src/ptrace/_UPT_resume.c index d70a0d48218f05..9ca5224cd800a9 100644 --- a/src/native/external/libunwind/src/ptrace/_UPT_resume.c +++ b/src/native/external/libunwind/src/ptrace/_UPT_resume.c @@ -26,10 +26,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "_UPT_internal.h" int -_UPT_resume (unw_addr_space_t as, unw_cursor_t *c, void *arg) +_UPT_resume (unw_addr_space_t as UNUSED, unw_cursor_t *c UNUSED, void *arg) { struct UPT_info *ui = arg; + mi_init (); + #ifdef HAVE_TTRACE # warning No support for ttrace() yet. #elif HAVE_DECL_PTRACE_CONT diff --git a/src/native/external/libunwind/src/remote/mac/missing-functions.c b/src/native/external/libunwind/src/remote/mac/missing-functions.c index f0a7b3f53f27be..119574cb79f834 100644 --- a/src/native/external/libunwind/src/remote/mac/missing-functions.c +++ b/src/native/external/libunwind/src/remote/mac/missing-functions.c @@ -74,3 +74,9 @@ UNW_OBJ(handle_signal_frame) (unw_cursor_t *cursor) { return -UNW_EBADFRAME; } + +int +UNW_OBJ(os_step) (struct cursor *c) +{ + return 0; +} diff --git a/src/native/external/libunwind/src/riscv/Gglobal.c b/src/native/external/libunwind/src/riscv/Gglobal.c index cb8009e1d2696e..9ad2e31884a414 100644 --- a/src/native/external/libunwind/src/riscv/Gglobal.c +++ b/src/native/external/libunwind/src/riscv/Gglobal.c @@ -117,8 +117,6 @@ tdep_init (void) dwarf_init (); #ifndef UNW_REMOTE_ONLY - tdep_init_mem_validate (); - riscv_local_addr_space_init (); #endif atomic_store(&tdep_init_done, 1); /* signal that we're initialized... */ diff --git a/src/native/external/libunwind/src/riscv/Ginit.c b/src/native/external/libunwind/src/riscv/Ginit.c index 4faeecbfd89f2b..861197be328e1a 100644 --- a/src/native/external/libunwind/src/riscv/Ginit.c +++ b/src/native/external/libunwind/src/riscv/Ginit.c @@ -95,233 +95,6 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, return 0; } -// Memory validation routines are from aarch64 - -static int mem_validate_pipe[2] = {-1, -1}; - -#ifdef HAVE_PIPE2 -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); -} -#else -static inline void -set_pipe_flags (int fd) -{ - int fd_flags = fcntl (fd, F_GETFD, 0); - int status_flags = fcntl (fd, F_GETFL, 0); - - fd_flags |= FD_CLOEXEC; - fcntl (fd, F_SETFD, fd_flags); - - status_flags |= O_NONBLOCK; - fcntl (fd, F_SETFL, status_flags); -} - -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe (pipefd); - set_pipe_flags(pipefd[0]); - set_pipe_flags(pipefd[1]); -} -#endif - -static inline void -open_pipe (void) -{ - if (mem_validate_pipe[0] != -1) - close (mem_validate_pipe[0]); - if (mem_validate_pipe[1] != -1) - close (mem_validate_pipe[1]); - - do_pipe2 (mem_validate_pipe); -} - -ALWAYS_INLINE -static int -write_validate (void *addr) -{ - int ret = -1; - ssize_t bytes = 0; - - do - { - char buf; - bytes = read (mem_validate_pipe[0], &buf, 1); - } - while ( errno == EINTR ); - - int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK); - if (!valid_read) - { - // re-open closed pipe - open_pipe (); - } - - do - { - ret = write (mem_validate_pipe[1], addr, 1); - } - while ( errno == EINTR ); - - return ret; -} - -static int (*mem_validate_func) (void *addr, size_t len); -static int msync_validate (void *addr, size_t len) -{ - if (msync (addr, len, MS_ASYNC) != 0) - { - return -1; - } - - return write_validate (addr); -} - -#ifdef HAVE_MINCORE -static int mincore_validate (void *addr, size_t len) -{ - unsigned char mvec[2]; /* Unaligned access may cross page boundary */ - - /* mincore could fail with EAGAIN but we conservatively return -1 - instead of looping. */ - if (mincore (addr, len, (unsigned char *)mvec) != 0) - { - return -1; - } - - return write_validate (addr); -} -#endif - -/* Initialise memory validation method. On linux kernels <2.6.21, - mincore() returns incorrect value for MAP_PRIVATE mappings, - such as stacks. If mincore() was available at compile time, - check if we can actually use it. If not, use msync() instead. */ -HIDDEN void -tdep_init_mem_validate (void) -{ - open_pipe (); - -#ifdef HAVE_MINCORE - unsigned char present = 1; - size_t len = unw_page_size; - unw_word_t addr = uwn_page_start((unw_word_t)&present); - unsigned char mvec[1]; - int ret; - while ((ret = mincore((void *)addr, len, (unsigned char *)mvec)) == -1 && - errno == EAGAIN) - { - } - if (ret == 0) - { - Debug(1, "using mincore to validate memory\n"); - mem_validate_func = mincore_validate; - } - else -#endif - { - Debug(1, "using msync to validate memory\n"); - mem_validate_func = msync_validate; - } -} - -/* Cache of already validated addresses */ -#define NLGA 4 -#if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD -// thread-local variant -static _Thread_local unw_word_t last_good_addr[NLGA]; -static _Thread_local int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == last_good_addr[i]) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (last_good_addr[victim] == 0) { - last_good_addr[victim] = addr; - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; -} - -#else -// global, thread safe variant -static _Atomic unw_word_t last_good_addr[NLGA]; -static _Atomic int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == atomic_load(&last_good_addr[i])) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = atomic_load(&lga_victim); - unw_word_t zero = 0; - for (i = 0; i < NLGA; i++) { - if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) { - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - atomic_store(&last_good_addr[victim], addr); - victim = (victim + 1) % NLGA; - atomic_store(&lga_victim, victim); -} -#endif - -static int -validate_mem (unw_word_t addr) -{ - size_t len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - if (is_cached_valid_mem(addr)) - return 0; - - if (mem_validate_func ((void *) addr, len) == -1) - return -1; - - cache_valid_mem(addr); - - return 0; -} static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, @@ -338,11 +111,11 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, const struct cursor *c = (const struct cursor *)arg; if (likely (c != NULL) && unlikely (c->validate) - && unlikely (validate_mem (addr))) { + && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) { Debug (16, "mem[%016lx] -> invalid\n", addr); return -1; } - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (16, "mem[%lx] -> %lx\n", addr, *val); } return 0; @@ -369,7 +142,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %lx\n", unw_regname (reg), *val); } return 0; @@ -420,11 +193,24 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return elf_w (get_elf_filename) (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void riscv_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.addr_size = sizeof (void *); local_addr_space.acc.find_proc_info = dwarf_find_proc_info; @@ -435,6 +221,7 @@ riscv_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = riscv_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; local_addr_space.big_endian = target_is_big_endian(); unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/s390x/Gglobal.c b/src/native/external/libunwind/src/s390x/Gglobal.c index 20544a57fa0172..e201c5228942b3 100644 --- a/src/native/external/libunwind/src/s390x/Gglobal.c +++ b/src/native/external/libunwind/src/s390x/Gglobal.c @@ -90,8 +90,6 @@ tdep_init (void) dwarf_init (); #ifndef UNW_REMOTE_ONLY - tdep_init_mem_validate (); - s390x_local_addr_space_init (); #endif atomic_store(&tdep_init_done, 1); /* signal that we're initialized... */ diff --git a/src/native/external/libunwind/src/s390x/Ginit.c b/src/native/external/libunwind/src/s390x/Ginit.c index 2bce2f4b687feb..3cf07a5d71ef60 100644 --- a/src/native/external/libunwind/src/s390x/Ginit.c +++ b/src/native/external/libunwind/src/s390x/Ginit.c @@ -94,155 +94,6 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, return 0; } -static int mem_validate_pipe[2] = {-1, -1}; - -static inline void -open_pipe (void) -{ - /* ignore errors for closing invalid fd's */ - close (mem_validate_pipe[0]); - close (mem_validate_pipe[1]); - - pipe2 (mem_validate_pipe, O_CLOEXEC | O_NONBLOCK); -} - -ALWAYS_INLINE -static int -write_validate (void *addr) -{ - int ret = -1; - ssize_t bytes = 0; - - do - { - char buf; - bytes = read (mem_validate_pipe[0], &buf, 1); - } - while ( errno == EINTR ); - - int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK); - if (!valid_read) - { - // re-open closed pipe - open_pipe (); - } - - do - { - /* use syscall insteadof write() so that ASAN does not complain */ - ret = syscall (SYS_write, mem_validate_pipe[1], addr, 1); - } - while ( errno == EINTR ); - - return ret; -} - -static int (*mem_validate_func) (void *addr, size_t len); -static int msync_validate (void *addr, size_t len) -{ - if (msync (addr, len, MS_ASYNC) != 0) - { - return -1; - } - - return write_validate (addr); -} - -#ifdef HAVE_MINCORE -static int mincore_validate (void *addr, size_t len) -{ - unsigned char mvec[2]; /* Unaligned access may cross page boundary */ - size_t i; - - /* mincore could fail with EAGAIN but we conservatively return -1 - instead of looping. */ - if (mincore (addr, len, mvec) != 0) - { - return -1; - } - - for (i = 0; i < (len + unw_page_size - 1) / unw_page_size; i++) - { - if (!(mvec[i] & 1)) return -1; - } - - return write_validate (addr); -} -#endif - -/* Initialise memory validation method. On linux kernels <2.6.21, - mincore() returns incorrect value for MAP_PRIVATE mappings, - such as stacks. If mincore() was available at compile time, - check if we can actually use it. If not, use msync() instead. */ -HIDDEN void -tdep_init_mem_validate (void) -{ - open_pipe (); - -#ifdef HAVE_MINCORE - unsigned char present = 1; - size_t len = unw_page_size; - unw_word_t addr = uwn_page_start((unw_word_t)&present); - unsigned char mvec[1]; - int ret; - while ((ret = mincore((void *)addr, len, mvec)) == -1 && - errno == EAGAIN) - { - } - if (ret == 0 && (mvec[0] & 1)) - { - Debug(1, "using mincore to validate memory\n"); - mem_validate_func = mincore_validate; - } - else -#endif - { - Debug(1, "using msync to validate memory\n"); - mem_validate_func = msync_validate; - } -} - -/* Cache of already validated addresses */ -#define NLGA 4 -static unw_word_t last_good_addr[NLGA]; -static int lga_victim; - -static int -validate_mem (unw_word_t addr) -{ - int i, victim; - size_t len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - for (i = 0; i < NLGA; i++) - { - if (last_good_addr[i] && (addr == last_good_addr[i])) - return 0; - } - - if (mem_validate_func ((void *) addr, len) == -1) - return -1; - - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (!last_good_addr[victim]) { - last_good_addr[victim++] = addr; - return 0; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; - - return 0; -} - static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, void *arg) @@ -250,18 +101,18 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (unlikely (write)) { Debug (16, "mem[%016lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { /* validate address */ const struct cursor *c = (const struct cursor *)arg; if (likely (c != NULL) && unlikely (c->validate) - && unlikely (validate_mem (addr))) { + && unlikely (!unw_address_is_valid (addr, sizeof(unw_word_t)))) { Debug (16, "mem[%016lx] -> invalid\n", addr); return -1; } - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (16, "mem[%016lx] -> %lx\n", addr, *val); } return 0; @@ -282,12 +133,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val); } return 0; @@ -338,10 +189,23 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf64_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void s390x_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNW_CACHE_GLOBAL; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -351,10 +215,8 @@ s390x_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = s390x_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); - - memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA); - lga_victim = 0; } #endif /* !UNW_REMOTE_ONLY */ diff --git a/src/native/external/libunwind/src/setjmp/longjmp.c b/src/native/external/libunwind/src/setjmp/longjmp.c index 8295a9b8ed40de..ced9052aa2f92b 100644 --- a/src/native/external/libunwind/src/setjmp/longjmp.c +++ b/src/native/external/libunwind/src/setjmp/longjmp.c @@ -54,6 +54,10 @@ static void longjmp (jmp_buf env, int val); #endif #endif /* __GLIBC__ */ +#ifndef _JB_STK_SHIFT +#define _JB_STK_SHIFT 0 +#endif + void _longjmp (jmp_buf env, int val) { @@ -70,11 +74,7 @@ _longjmp (jmp_buf env, int val) { if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) abort (); -#ifdef __FreeBSD__ - if (sp != wp[JB_SP] + sizeof(unw_word_t)) -#else - if (sp != wp[JB_SP]) -#endif + if (sp != (wp[JB_SP] + _JB_STK_SHIFT)) continue; if (!bsp_match (&c, wp)) diff --git a/src/native/external/libunwind/src/setjmp/setjmp_i.h b/src/native/external/libunwind/src/setjmp/setjmp_i.h index 4d9139693ecfb7..3c19185ed9d0d8 100644 --- a/src/native/external/libunwind/src/setjmp/setjmp_i.h +++ b/src/native/external/libunwind/src/setjmp/setjmp_i.h @@ -23,10 +23,13 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#if UNW_TARGET_IA64 +#ifndef libunwind_setjmp_setjmp_i_h +#define libunwind_setjmp_setjmp_i_h #include "libunwind_i.h" -#include "tdep-ia64/rse.h" + +#if UNW_TARGET_IA64 +# include "tdep-ia64/rse.h" static inline int bsp_match (unw_cursor_t *c, unw_word_t *wp) @@ -103,16 +106,18 @@ resume_restores_sigmask (unw_cursor_t *c, unw_word_t *wp) #else /* !UNW_TARGET_IA64 */ static inline int -bsp_match (unw_cursor_t *c, unw_word_t *wp) +bsp_match (unw_cursor_t *c UNUSED, unw_word_t *wp UNUSED) { return 1; } static inline int -resume_restores_sigmask (unw_cursor_t *c, unw_word_t *wp) +resume_restores_sigmask (unw_cursor_t *c UNUSED, unw_word_t *wp UNUSED) { /* We may want to do this analogously as for ia64... */ return 0; } #endif /* !UNW_TARGET_IA64 */ + +#endif /* libunwind_setjmp_setjmp_i_h */ diff --git a/src/native/external/libunwind/src/setjmp/siglongjmp.c b/src/native/external/libunwind/src/setjmp/siglongjmp.c index dd330ce9e19db6..05e18176fe751f 100644 --- a/src/native/external/libunwind/src/setjmp/siglongjmp.c +++ b/src/native/external/libunwind/src/setjmp/siglongjmp.c @@ -57,6 +57,10 @@ static void siglongjmp (sigjmp_buf env, int val) UNUSED; #endif #endif /* __GLIBC_PREREQ */ +#ifndef _JB_STK_SHIFT +#define _JB_STK_SHIFT 0 +#endif + void siglongjmp (sigjmp_buf env, int val) { @@ -75,11 +79,7 @@ siglongjmp (sigjmp_buf env, int val) { if (unw_get_reg (&c, UNW_REG_SP, &sp) < 0) abort (); -#ifdef __FreeBSD__ - if (sp != wp[JB_SP] + sizeof(unw_word_t)) -#else - if (sp != wp[JB_SP]) -#endif + if (sp != (wp[JB_SP] + _JB_STK_SHIFT)) continue; if (!bsp_match (&c, wp)) @@ -93,7 +93,11 @@ siglongjmp (sigjmp_buf env, int val) /* Order of evaluation is important here: if unw_resume() restores signal mask, we must set it up appropriately, even if wp[JB_MASK_SAVED] is FALSE. */ +#ifdef __FreeBSD__ + if ((wp[JB_MASK_SAVED] & 0x1) == 0x1) +#else if (!resume_restores_sigmask (&c, wp) && wp[JB_MASK_SAVED]) +#endif { /* sigmask was saved */ #if defined(__linux__) || defined(__sun) @@ -108,7 +112,7 @@ siglongjmp (sigjmp_buf env, int val) && unw_set_reg (&c, UNW_REG_EH + 3, wp[JB_MASK + 1]) < 0)) abort (); #elif defined(__FreeBSD__) - if (unw_set_reg (&c, UNW_REG_EH + 2, &wp[JB_MASK]) < 0) + if (unw_set_reg (&c, UNW_REG_EH + 2, (unw_word_t)&wp[JB_MASK]) < 0) abort(); #else #error Port me diff --git a/src/native/external/libunwind/src/sh/Ginit.c b/src/native/external/libunwind/src/sh/Ginit.c index 9fe96d2bd4d8eb..ba62484c5be486 100644 --- a/src/native/external/libunwind/src/sh/Ginit.c +++ b/src/native/external/libunwind/src/sh/Ginit.c @@ -85,11 +85,11 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (write) { Debug (16, "mem[%x] <- %x\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (16, "mem[%x] -> %x\n", addr, *val); } return 0; @@ -110,12 +110,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %x\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %x\n", unw_regname (reg), *val); } return 0; @@ -166,10 +166,23 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf32_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void sh_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -179,6 +192,7 @@ sh_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = sh_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/tilegx/Gapply_reg_state.c b/src/native/external/libunwind/src/tilegx/Gapply_reg_state.c deleted file mode 100644 index 82f056da67ebf5..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gapply_reg_state.c +++ /dev/null @@ -1,37 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (c) 2002-2003 Hewlett-Packard Development Company, L.P. - Contributed by David Mosberger-Tang - - Modified for x86_64 by Max Asbock - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -int -unw_apply_reg_state (unw_cursor_t *cursor, - void *reg_states_data) -{ - struct cursor *c = (struct cursor *) cursor; - - return dwarf_apply_reg_state (&c->dwarf, (dwarf_reg_state_t *)reg_states_data); -} diff --git a/src/native/external/libunwind/src/tilegx/Gcreate_addr_space.c b/src/native/external/libunwind/src/tilegx/Gcreate_addr_space.c deleted file mode 100644 index a37c1282f610f8..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gcreate_addr_space.c +++ /dev/null @@ -1,62 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include - -#include "unwind_i.h" - -unw_addr_space_t -unw_create_addr_space (unw_accessors_t *a, int byte_order) -{ -#ifdef UNW_LOCAL_ONLY - return NULL; -#else - /* - * Tilegx supports only big or little-endian, not weird stuff like - * PDP_ENDIAN. - */ - if (byte_order != 0 && byte_order_is_valid(byte_order) == 0) - return NULL; - - unw_addr_space_t as = malloc (sizeof (*as)); - if (!as) - return NULL; - - memset (as, 0, sizeof (*as)); - - as->acc = *a; - - if (byte_order == 0) - /* use host default: */ - as->big_endian = target_is_big_endian(); - else - as->big_endian = (byte_order == UNW_BIG_ENDIAN); - - as->abi = UNW_TILEGX_ABI_N64; - as->addr_size = 8; - - return as; -#endif -} diff --git a/src/native/external/libunwind/src/tilegx/Gget_proc_info.c b/src/native/external/libunwind/src/tilegx/Gget_proc_info.c deleted file mode 100644 index 3a158da2df7aa6..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gget_proc_info.c +++ /dev/null @@ -1,48 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -int -unw_get_proc_info (unw_cursor_t *cursor, unw_proc_info_t *pi) -{ - struct cursor *c = (struct cursor *) cursor; - int ret; - - ret = dwarf_make_proc_info (&c->dwarf); - - if (ret < 0) - { - /* On Tilegx, some routines i.e. _start() etc has no dwarf info. - Just simply mark the end of the frames. */ - memset (pi, 0, sizeof (*pi)); - pi->start_ip = c->dwarf.ip; - pi->end_ip = c->dwarf.ip + 1; - return 0; - } - - *pi = c->dwarf.pi; - return 0; -} diff --git a/src/native/external/libunwind/src/tilegx/Gget_save_loc.c b/src/native/external/libunwind/src/tilegx/Gget_save_loc.c deleted file mode 100644 index fcf0697892880b..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gget_save_loc.c +++ /dev/null @@ -1,62 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -int -unw_get_save_loc (unw_cursor_t *cursor, int reg, unw_save_loc_t *sloc) -{ - struct cursor *c = (struct cursor *) cursor; - dwarf_loc_t loc; - - loc = DWARF_NULL_LOC; /* default to "not saved" */ - - if (reg <= UNW_TILEGX_R55) - loc = c->dwarf.loc[reg - UNW_TILEGX_R0]; - else - printf("\nInvalid register!"); - - memset (sloc, 0, sizeof (*sloc)); - - if (DWARF_IS_NULL_LOC (loc)) - { - sloc->type = UNW_SLT_NONE; - return 0; - } - -#if !defined(UNW_LOCAL_ONLY) - if (DWARF_IS_REG_LOC (loc)) - { - sloc->type = UNW_SLT_REG; - sloc->u.regnum = DWARF_GET_LOC (loc); - } - else -#endif - { - sloc->type = UNW_SLT_MEMORY; - sloc->u.addr = DWARF_GET_LOC (loc); - } - return 0; -} diff --git a/src/native/external/libunwind/src/tilegx/Gglobal.c b/src/native/external/libunwind/src/tilegx/Gglobal.c deleted file mode 100644 index 5232d41c23bba9..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gglobal.c +++ /dev/null @@ -1,64 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" -#include "dwarf_i.h" - -__attribute__((weak)) -pthread_mutex_t tilegx_lock = PTHREAD_MUTEX_INITIALIZER; -HIDDEN atomic_bool tdep_init_done = 0; - -HIDDEN const uint8_t dwarf_to_unw_regnum_map[] = - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55 - }; - -HIDDEN void -tdep_init (void) -{ - intrmask_t saved_mask; - - sigfillset (&unwi_full_mask); - - lock_acquire (&tilegx_lock, saved_mask); - - if (atomic_load(&tdep_init_done)) - /* another thread else beat us to it... */ - goto out; - - mi_init (); - dwarf_init (); - -#ifndef UNW_REMOTE_ONLY - tilegx_local_addr_space_init (); -#endif - atomic_store(&tdep_init_done, 1); /* signal that we're initialized... */ - - out: - lock_release (&tilegx_lock, saved_mask); -} diff --git a/src/native/external/libunwind/src/tilegx/Ginit.c b/src/native/external/libunwind/src/tilegx/Ginit.c deleted file mode 100644 index 9d6c92ad4f91ec..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Ginit.c +++ /dev/null @@ -1,166 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include -#include - -#include "unwind_i.h" - -#ifdef UNW_REMOTE_ONLY - -/* unw_local_addr_space is a NULL pointer in this case. */ -unw_addr_space_t unw_local_addr_space; - -#else /* !UNW_REMOTE_ONLY */ - -static struct unw_addr_space local_addr_space; - -unw_addr_space_t unw_local_addr_space = &local_addr_space; - -/* Return the address of the 64-bit slot in UC for REG (even for o32, - where registers are 32-bit, the slots are still 64-bit). */ - -static inline void * -uc_addr (ucontext_t *uc, int reg) -{ - if (reg >= UNW_TILEGX_R0 && reg < UNW_TILEGX_R0 + 56) - return &uc->uc_mcontext.gregs[reg - UNW_TILEGX_R0]; - else if (reg == UNW_TILEGX_PC) - return &uc->uc_mcontext.pc; - else - return NULL; -} - -# ifdef UNW_LOCAL_ONLY - -HIDDEN void * -tdep_uc_addr (ucontext_t *uc, int reg) -{ - char *addr = uc_addr (uc, reg); - return addr; -} - -# endif /* UNW_LOCAL_ONLY */ - -static void -put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) -{ - /* it's a no-op */ -} - -static int -get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, - void *arg) -{ -#ifndef UNW_LOCAL_ONLY -# pragma weak _U_dyn_info_list_addr - if (!_U_dyn_info_list_addr) - return -UNW_ENOINFO; -#endif - // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so. - *dyn_info_list_addr = _U_dyn_info_list_addr (); - return 0; -} - -static int -access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, - void *arg) -{ - if ((long long)addr & (sizeof(unw_word_t) - 1)) - return 0; - - if (write) - { - Debug (16, "mem[%llx] <- %llx\n", (long long) addr, (long long) *val); - *(unw_word_t *) (intptr_t) addr = *val; - } - else - { - *val = *(unw_word_t *) (intptr_t) addr; - Debug (16, "mem[%llx] -> %llx\n", (long long) addr, (long long) *val); - } - return 0; -} - -static int -access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, - void *arg) -{ - unw_word_t *addr; - ucontext_t *uc = arg; - - if (unw_is_fpreg (reg)) - goto badreg; - - Debug (16, "reg = %s\n", unw_regname (reg)); - if (!(addr = uc_addr (uc, reg))) - goto badreg; - - if (write) - { - *(unw_word_t *) (intptr_t) addr = (tilegx_reg_t) *val; - Debug (12, "%s <- %llx\n", unw_regname (reg), (long long) *val); - } - else - { - *val = (tilegx_reg_t) *(unw_word_t *) (intptr_t) addr; - Debug (12, "%s -> %llx\n", unw_regname (reg), (long long) *val); - } - return 0; - - badreg: - Debug (1, "bad register number %u\n", reg); - return -UNW_EBADREG; -} - -static int -get_static_proc_name (unw_addr_space_t as, unw_word_t ip, - char *buf, size_t buf_len, unw_word_t *offp, - void *arg) -{ - return elf_w (get_proc_name) (as, getpid (), ip, buf, buf_len, offp); -} - -__attribute__((weak)) void -tilegx_local_addr_space_init (void) -{ - memset (&local_addr_space, 0, sizeof (local_addr_space)); - local_addr_space.big_endian = target_is_big_endian(); - - local_addr_space.abi = UNW_TILEGX_ABI_N64; - local_addr_space.addr_size = sizeof (void *); - local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; - local_addr_space.acc.find_proc_info = dwarf_find_proc_info; - local_addr_space.acc.put_unwind_info = put_unwind_info; - local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr; - local_addr_space.acc.access_mem = access_mem; - local_addr_space.acc.access_reg = access_reg; - local_addr_space.acc.access_fpreg = NULL; - local_addr_space.acc.resume = tilegx_local_resume; - local_addr_space.acc.get_proc_name = get_static_proc_name; - unw_flush_cache (&local_addr_space, 0, 0); -} - -#endif /* !UNW_REMOTE_ONLY */ diff --git a/src/native/external/libunwind/src/tilegx/Ginit_remote.c b/src/native/external/libunwind/src/tilegx/Ginit_remote.c deleted file mode 100644 index d6a874c13e82bd..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Ginit_remote.c +++ /dev/null @@ -1,47 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "init.h" -#include "unwind_i.h" - -int -unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) -{ -#ifdef UNW_LOCAL_ONLY - return -UNW_EINVAL; -#else /* !UNW_LOCAL_ONLY */ - struct cursor *c = (struct cursor *) cursor; - - if (!atomic_load(&tdep_init_done)) - tdep_init (); - - Debug (1, "(cursor=%p)\n", c); - - c->dwarf.as = as; - c->dwarf.as_arg = as_arg; - - return common_init (c, 0); -#endif /* !UNW_LOCAL_ONLY */ -} diff --git a/src/native/external/libunwind/src/tilegx/Gis_signal_frame.c b/src/native/external/libunwind/src/tilegx/Gis_signal_frame.c deleted file mode 100644 index eea00e47fc1779..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gis_signal_frame.c +++ /dev/null @@ -1,118 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" -#include -#include "offsets.h" - -#ifdef __linux__ -#include -#include -#else -# error "Only support Linux!" -#endif - -#define MOVELI_R10_RT_SIGRETURN \ - ( 0x000007e051483000ULL | \ - ((unsigned long)__NR_rt_sigreturn << 43) | \ - ((unsigned long)TREG_SYSCALL_NR << 31) ) -#define SWINT1 0x286b180051485000ULL - -int -unw_is_signal_frame (unw_cursor_t *cursor) -{ - struct cursor *c = (struct cursor*) cursor; - unw_word_t w0, w1, ip; - unw_addr_space_t as; - unw_accessors_t *a; - void *arg; - int ret; - - as = c->dwarf.as; - a = unw_get_accessors_int (as); - arg = c->dwarf.as_arg; - - ip = c->dwarf.ip; - - if (!ip || !a->access_mem || (ip & (sizeof(unw_word_t) - 1))) - return 0; - - if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0) - return ret; - - if ((ret = (*a->access_mem) (as, ip + 8, &w1, 0, arg)) < 0) - return ret; - - /* Return 1 if the IP points to a RT sigreturn sequence. */ - if (w0 == MOVELI_R10_RT_SIGRETURN && - w1 == SWINT1) - { - return 1; - } - return 0; -} - - -HIDDEN int -tilegx_handle_signal_frame (unw_cursor_t *cursor) -{ - int i; - struct cursor *c = (struct cursor *) cursor; - unw_word_t sc_addr, sp, sp_addr = c->dwarf.cfa; - struct dwarf_loc sp_loc = DWARF_LOC (sp_addr, 0); - int ret; - - if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0) - return -UNW_EUNSPEC; - - /* Save the SP and PC to be able to return execution at this point - later in time (unw_resume). */ - c->sigcontext_sp = c->dwarf.cfa; - c->sigcontext_pc = c->dwarf.ip; - - c->sigcontext_addr = sp_addr + sizeof (siginfo_t) + - C_ABI_SAVE_AREA_SIZE; - sc_addr = c->sigcontext_addr + LINUX_UC_MCONTEXT_OFF; - - for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) - c->dwarf.loc[i] = DWARF_NULL_LOC; - - /* Update the dwarf cursor. - Set the location of the registers to the corresponding addresses of the - uc_mcontext / sigcontext structure contents. */ - -#define SC_REG_OFFSET(X) (8 * X) - - for (i = UNW_TILEGX_R0; i <= UNW_TILEGX_R55; i++) - { - c->dwarf.loc[i] = DWARF_LOC (sc_addr + SC_REG_OFFSET(i), 0); - } - - /* Set SP/CFA and PC/IP. */ - dwarf_get (&c->dwarf, c->dwarf.loc[UNW_TILEGX_R54], &c->dwarf.cfa); - dwarf_get (&c->dwarf, c->dwarf.loc[UNW_TILEGX_R55], &c->dwarf.ip); - - return 1; -} diff --git a/src/native/external/libunwind/src/tilegx/Greg_states_iterate.c b/src/native/external/libunwind/src/tilegx/Greg_states_iterate.c deleted file mode 100644 index a17dc1b561d6f8..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Greg_states_iterate.c +++ /dev/null @@ -1,37 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (c) 2002-2003 Hewlett-Packard Development Company, L.P. - Contributed by David Mosberger-Tang - - Modified for x86_64 by Max Asbock - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -int -unw_reg_states_iterate (unw_cursor_t *cursor, - unw_reg_states_callback cb, void *token) -{ - struct cursor *c = (struct cursor *) cursor; - - return dwarf_reg_states_iterate (&c->dwarf, cb, token); -} diff --git a/src/native/external/libunwind/src/tilegx/Gregs.c b/src/native/external/libunwind/src/tilegx/Gregs.c deleted file mode 100644 index 565c6f4432a28e..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gregs.c +++ /dev/null @@ -1,76 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -HIDDEN int -tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, - int write) -{ - dwarf_loc_t loc = DWARF_NULL_LOC; - - if (reg == UNW_TILEGX_R54 && !write) - { - reg = UNW_TILEGX_CFA; - } - - if (reg <= UNW_TILEGX_R55) - loc = c->dwarf.loc[reg - UNW_TILEGX_R0]; - else if (reg == UNW_TILEGX_CFA) - { - if (write) - return -UNW_EREADONLYREG; - *valp = c->dwarf.cfa; - return 0; - } - else - { - Debug (1, "bad register number %u\n", reg); - return -UNW_EBADREG; - } - - if (write) - { - if (ci->dwarf.use_prev_instr == 0) { - if (reg == UNW_TILEGX_PC) - c->dwarf.ip = *valp; /* update the IP cache */ - } - else { - if (reg == UNW_TILEGX_R55) - c->dwarf.ip = *valp; /* update the IP cache */ - } - return dwarf_put (&c->dwarf, loc, *valp); - } - else - return dwarf_get (&c->dwarf, loc, valp); -} - -HIDDEN int -tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, unw_fpreg_t *valp, - int write) -{ - Debug (1, "bad register number %u\n", reg); - return -UNW_EBADREG; -} diff --git a/src/native/external/libunwind/src/tilegx/Gresume.c b/src/native/external/libunwind/src/tilegx/Gresume.c deleted file mode 100644 index ece443a5b56fc9..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gresume.c +++ /dev/null @@ -1,94 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - - -#include "unwind_i.h" -#include "offsets.h" -#include - -#ifndef UNW_REMOTE_ONLY - -HIDDEN inline int -tilegx_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) -{ - int i; - struct cursor *c = (struct cursor *) cursor; - ucontext_t *uc = c->dwarf.as_arg; - - Debug (1, "(cursor=%p\n", c); - - return setcontext(uc); -} - -#endif /* !UNW_REMOTE_ONLY */ - -static inline void -establish_machine_state (struct cursor *c) -{ - unw_addr_space_t as = c->dwarf.as; - void *arg = c->dwarf.as_arg; - unw_fpreg_t fpval; - unw_word_t val; - int reg; - - Debug (8, "copying out cursor state\n"); - - for (reg = 0; reg <= UNW_REG_LAST; ++reg) - { - Debug (16, "copying %s %d\n", unw_regname (reg), reg); - - if (unw_is_fpreg (reg)) - { - Debug (1, "no fp!"); - abort (); - } - else - { - if (tdep_access_reg (c, reg, &val, 0) >= 0) - as->acc.access_reg (as, reg, &val, 1, arg); - } - } -} - -int -unw_resume (unw_cursor_t *cursor) -{ - struct cursor *c = (struct cursor *) cursor; - - Debug (1, "(cursor=%p) ip=0x%lx\n", c, c->dwarf.ip); - - if (!c->dwarf.ip) - { - /* This can happen easily when the frame-chain gets truncated - due to bad or missing unwind-info. */ - Debug (1, "refusing to resume execution at address 0\n"); - return -UNW_EINVAL; - } - - establish_machine_state (c); - - return (*c->dwarf.as->acc.resume) (c->dwarf.as, (unw_cursor_t *) c, - c->dwarf.as_arg); -} diff --git a/src/native/external/libunwind/src/tilegx/Gstep.c b/src/native/external/libunwind/src/tilegx/Gstep.c deleted file mode 100644 index b4456897bed0a4..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Gstep.c +++ /dev/null @@ -1,53 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" -#include "offsets.h" - -int -unw_step (unw_cursor_t *cursor) -{ - struct cursor *c = (struct cursor *) cursor; - int ret; - - Debug (1, "(cursor=%p, ip=0x%016lx, sp=0x%016lx)\n", - c, c->dwarf.ip, c->dwarf.cfa); - - /* Special handling the signal frame. */ - if (unw_is_signal_frame (cursor) > 0) - return tilegx_handle_signal_frame (cursor); - - /* Try DWARF-based unwinding... */ - ret = dwarf_step (&c->dwarf); - - if (unlikely (ret == -UNW_ESTOPUNWIND)) - return ret; - - /* Dwarf unwinding didn't work, stop. */ - if (unlikely (ret < 0)) - return 0; - - return (c->dwarf.ip == 0) ? 0 : 1; -} diff --git a/src/native/external/libunwind/src/tilegx/Lget_save_loc.c b/src/native/external/libunwind/src/tilegx/Lget_save_loc.c deleted file mode 100644 index 9ea048a9076ba8..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Lget_save_loc.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gget_save_loc.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/Linit_remote.c b/src/native/external/libunwind/src/tilegx/Linit_remote.c deleted file mode 100644 index 58cb04ab7cd1fd..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Linit_remote.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Ginit_remote.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/Lis_signal_frame.c b/src/native/external/libunwind/src/tilegx/Lis_signal_frame.c deleted file mode 100644 index b9a7c4f51ad9fa..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Lis_signal_frame.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gis_signal_frame.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/Lreg_states_iterate.c b/src/native/external/libunwind/src/tilegx/Lreg_states_iterate.c deleted file mode 100644 index f1eb1e79dcdcca..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Lreg_states_iterate.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Greg_states_iterate.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/Lregs.c b/src/native/external/libunwind/src/tilegx/Lregs.c deleted file mode 100644 index 2c9c75cd7d9a1e..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Lregs.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gregs.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/Lresume.c b/src/native/external/libunwind/src/tilegx/Lresume.c deleted file mode 100644 index 41a8cf003de4ac..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Lresume.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gresume.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/Lstep.c b/src/native/external/libunwind/src/tilegx/Lstep.c deleted file mode 100644 index c1ac3c7547f00d..00000000000000 --- a/src/native/external/libunwind/src/tilegx/Lstep.c +++ /dev/null @@ -1,5 +0,0 @@ -#define UNW_LOCAL_ONLY -#include -#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) -#include "Gstep.c" -#endif diff --git a/src/native/external/libunwind/src/tilegx/elfxx.c b/src/native/external/libunwind/src/tilegx/elfxx.c deleted file mode 100644 index 07d3d12b94fe00..00000000000000 --- a/src/native/external/libunwind/src/tilegx/elfxx.c +++ /dev/null @@ -1,27 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "libunwind_i.h" - -#include "../src/elfxx.c" diff --git a/src/native/external/libunwind/src/tilegx/gen-offsets.c b/src/native/external/libunwind/src/tilegx/gen-offsets.c deleted file mode 100644 index 8704bb215e3cb7..00000000000000 --- a/src/native/external/libunwind/src/tilegx/gen-offsets.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include - -#define UC(N,X) \ - printf ("#define LINUX_UC_" N "_OFF\t0x%X\n", offsetof (ucontext_t, X)) - -#define SC(N,X) \ - printf ("#define LINUX_SC_" N "_OFF\t0x%X\n", offsetof (struct sigcontext, X)) - -int -main (void) -{ - printf ( -"/* Linux-specific definitions: */\n\n" - -"/* Define various structure offsets to simplify cross-compilation. */\n\n" - -"/* Offsets for TILEGX Linux \"ucontext_t\": */\n\n"); - - UC ("FLAGS", uc_flags); - UC ("LINK", uc_link); - UC ("STACK", uc_stack); - UC ("MCONTEXT", uc_mcontext); - UC ("SIGMASK", uc_sigmask); - - UC ("MCONTEXT_GREGS", uc_mcontext.gregs); - - return 0; -} diff --git a/src/native/external/libunwind/src/tilegx/getcontext.S b/src/native/external/libunwind/src/tilegx/getcontext.S deleted file mode 100644 index fbc8654bc7f1f9..00000000000000 --- a/src/native/external/libunwind/src/tilegx/getcontext.S +++ /dev/null @@ -1,36 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "offsets.h" -#include - - .text - # define REG(X) LINUX_UC_MCONTEXT_GREGS + 8 * (X) - .global _Utilegx_getcontext - .type _Utilegx_getcontext, %function - # This is a stub version of getcontext() for TILEGX. -_Utilegx_getcontext: - - diff --git a/src/native/external/libunwind/src/tilegx/init.h b/src/native/external/libunwind/src/tilegx/init.h deleted file mode 100644 index 0e0f7fd1da819c..00000000000000 --- a/src/native/external/libunwind/src/tilegx/init.h +++ /dev/null @@ -1,63 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -static inline int -common_init (struct cursor *c, unsigned use_prev_instr) -{ - int ret, i; - - for (i = 0; i < 56; i++) - c->dwarf.loc[i] = DWARF_REG_LOC (&c->dwarf, UNW_TILEGX_R0 + i); - for (i = 56; i < DWARF_NUM_PRESERVED_REGS; ++i) - c->dwarf.loc[i] = DWARF_NULL_LOC; - - if (use_prev_instr == 0) - ret = dwarf_get (&c->dwarf, DWARF_REG_LOC (&c->dwarf, UNW_TILEGX_PC), - &c->dwarf.ip); - else - ret = dwarf_get (&c->dwarf, DWARF_REG_LOC (&c->dwarf, UNW_TILEGX_R55), - &c->dwarf.ip); - - if (ret < 0) - return ret; - - ret = dwarf_get (&c->dwarf, DWARF_REG_LOC (&c->dwarf, UNW_TILEGX_R54), - &c->dwarf.cfa); - - if (ret < 0) - return ret; - - c->dwarf.args_size = 0; - c->dwarf.stash_frames = 0; - c->dwarf.use_prev_instr = use_prev_instr; - c->dwarf.pi_valid = 0; - c->dwarf.pi_is_dynamic = 0; - c->dwarf.hint = 0; - c->dwarf.prev_rs = 0; - - return 0; -} diff --git a/src/native/external/libunwind/src/tilegx/offsets.h b/src/native/external/libunwind/src/tilegx/offsets.h deleted file mode 100644 index 6d30f1edcff1ef..00000000000000 --- a/src/native/external/libunwind/src/tilegx/offsets.h +++ /dev/null @@ -1,12 +0,0 @@ -/* Linux-specific definitions: */ - -/* Define various structure offsets to simplify cross-compilation. */ - -/* Offsets for TILEGX Linux "ucontext_t": */ - -#define LINUX_UC_FLAGS_OFF 0x0 -#define LINUX_UC_LINK_OFF 0x8 -#define LINUX_UC_STACK_OFF 0x10 -#define LINUX_UC_MCONTEXT_OFF 0x28 -#define LINUX_UC_SIGMASK_OFF 0x228 -#define LINUX_UC_MCONTEXT_GREGS 0x28 diff --git a/src/native/external/libunwind/src/tilegx/regname.c b/src/native/external/libunwind/src/tilegx/regname.c deleted file mode 100644 index 0ce47b9d66ed85..00000000000000 --- a/src/native/external/libunwind/src/tilegx/regname.c +++ /dev/null @@ -1,55 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - Copyright (C) 2014 Tilera Corp. - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "unwind_i.h" - -static const char *regname[] = - { - /* 0. */ - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - /* 8. */ - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - /* 16. */ - "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", - /* 24. */ - "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", - /* 32. */ - "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", - /* 40. */ - "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", - /* 48. */ - "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", - /* pc, cfa */ - "pc", "cfa" - }; - -const char * -unw_regname (unw_regnum_t reg) -{ - if (reg < (unw_regnum_t) ARRAY_SIZE (regname)) - return regname[reg]; - else - return "???"; -} diff --git a/src/native/external/libunwind/src/tilegx/siglongjmp.S b/src/native/external/libunwind/src/tilegx/siglongjmp.S deleted file mode 100644 index bccb1c77854339..00000000000000 --- a/src/native/external/libunwind/src/tilegx/siglongjmp.S +++ /dev/null @@ -1,7 +0,0 @@ - /* Dummy implementation for now. */ - .globl _UI_siglongjmp_cont - .globl _UI_longjmp_cont - -_UI_siglongjmp_cont: -_UI_longjmp_cont: - jrp lr diff --git a/src/native/external/libunwind/src/tilegx/unwind_i.h b/src/native/external/libunwind/src/tilegx/unwind_i.h deleted file mode 100644 index 9d41c90b4d10bc..00000000000000 --- a/src/native/external/libunwind/src/tilegx/unwind_i.h +++ /dev/null @@ -1,46 +0,0 @@ -/* libunwind - a platform-independent unwind library - Copyright (C) 2008 CodeSourcery - -This file is part of libunwind. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef unwind_i_h -#define unwind_i_h - -#include -#include - -#include - -#include "libunwind_i.h" - -#define tilegx_local_resume UNW_OBJ(local_resume) -#define tilegx_local_addr_space_init UNW_OBJ(local_addr_space_init) - -extern int tilegx_local_resume (unw_addr_space_t as, - unw_cursor_t *cursor, - void *arg); -#define tilegx_handle_signal_frame UNW_OBJ(handle_signal_frame) -extern int tilegx_handle_signal_frame(unw_cursor_t *cursor); - -extern void tilegx_local_addr_space_init (void); - -#endif /* unwind_i_h */ diff --git a/src/native/external/libunwind/src/x86/Ginit.c b/src/native/external/libunwind/src/x86/Ginit.c index 4261fb5235aa3c..956d8088b6afe9 100644 --- a/src/native/external/libunwind/src/x86/Ginit.c +++ b/src/native/external/libunwind/src/x86/Ginit.c @@ -74,53 +74,6 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, return 0; } -/* Cache of already validated addresses */ -#define NLGA 4 -static unw_word_t last_good_addr[NLGA]; -static int lga_victim; - -static int -validate_mem (unw_word_t addr) -{ - int i, victim; -#ifdef HAVE_MINCORE - unsigned char mvec[2]; /* Unaligned access may cross page boundary */ -#endif - size_t len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - for (i = 0; i < NLGA; i++) - { - if (last_good_addr[i] && (addr == last_good_addr[i])) - return 0; - } - -#ifdef HAVE_MINCORE - if (mincore ((void *) addr, len, mvec) == -1) -#else - if (msync ((void *) addr, len, MS_ASYNC) == -1) -#endif - return -1; - - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (!last_good_addr[victim]) { - last_good_addr[victim++] = addr; - return 0; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; - - return 0; -} static int access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, @@ -129,15 +82,18 @@ access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, if (write) { Debug (16, "mem[%x] <- %x\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { /* validate address */ const struct cursor *c = (const struct cursor *)arg; - if (c && c->validate && validate_mem(addr)) - return -1; - *val = *(unw_word_t *) addr; + if (unlikely (c && c->validate) && !unw_address_is_valid (addr, sizeof(unw_word_t))) + { + Debug (16, "mem[%#010lx] -> invalid\n", (long)addr); + return -1; + } + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (16, "mem[%x] -> %x\n", addr, *val); } return 0; @@ -158,12 +114,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- %x\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> %x\n", unw_regname (reg), *val); } return 0; @@ -214,10 +170,23 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip, return _Uelf32_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg) +{ + return _Uelf32_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void x86_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -227,6 +196,7 @@ x86_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = x86_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/x86/Gos-freebsd.c b/src/native/external/libunwind/src/x86/Gos-freebsd.c index 28e6722af161a5..ca95f17c20af43 100644 --- a/src/native/external/libunwind/src/x86/Gos-freebsd.c +++ b/src/native/external/libunwind/src/x86/Gos-freebsd.c @@ -111,6 +111,7 @@ x86_handle_signal_frame (unw_cursor_t *cursor) struct sigframe *sf; uintptr_t uc_addr; struct dwarf_loc esp_loc; + int i; sf = (struct sigframe *)c->dwarf.cfa; uc_addr = (uintptr_t)&(sf->sf_uc); @@ -124,7 +125,7 @@ x86_handle_signal_frame (unw_cursor_t *cursor) return 0; } - for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) c->dwarf.loc[i] = DWARF_NULL_LOC; c->dwarf.loc[EIP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EIP_OFF, 0); diff --git a/src/native/external/libunwind/src/x86/Gos-linux.c b/src/native/external/libunwind/src/x86/Gos-linux.c index 0908e3a11d6ca2..725ef73827f33d 100644 --- a/src/native/external/libunwind/src/x86/Gos-linux.c +++ b/src/native/external/libunwind/src/x86/Gos-linux.c @@ -136,6 +136,10 @@ x86_handle_signal_frame (unw_cursor_t *cursor) c->dwarf.loc[EIP] = DWARF_LOC (sc_addr + LINUX_SC_EIP_OFF, 0); c->dwarf.loc[ESP] = DWARF_LOC (sc_addr + LINUX_SC_ESP_OFF, 0); + ret = dwarf_get (&c->dwarf, c->dwarf.loc[EIP], &c->dwarf.ip); + ret = dwarf_get (&c->dwarf, c->dwarf.loc[ESP], &c->dwarf.cfa); + c->dwarf.use_prev_instr = 0; + return 0; } @@ -287,49 +291,11 @@ x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) struct cursor *c = (struct cursor *) cursor; ucontext_t *uc = c->uc; - /* Ensure c->pi is up-to-date. On x86, it's relatively common to be - missing DWARF unwind info. We don't want to fail in that case, - because the frame-chain still would let us do a backtrace at - least. */ - dwarf_make_proc_info (&c->dwarf); - - if (unlikely (c->sigcontext_format != X86_SCF_NONE)) - { - struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; - - Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc); + Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip); #if !defined(__ANDROID__) - x86_sigreturn (sc); + setcontext (uc); #endif - } - else - { - Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip); -#if !defined(__ANDROID__) - setcontext (uc); -#endif - } return -UNW_EINVAL; } -/* sigreturn() is a no-op on x86 glibc. */ -HIDDEN void -x86_sigreturn (unw_cursor_t *cursor) -{ - struct cursor *c = (struct cursor *) cursor; - struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; - mcontext_t *sc_mcontext = &((ucontext_t*)sc)->uc_mcontext; - /* Copy in saved uc - all preserved regs are at the start of sigcontext */ - memcpy(sc_mcontext, &c->uc->uc_mcontext, - DWARF_NUM_PRESERVED_REGS * sizeof(unw_word_t)); - - Debug (8, "resuming at ip=%llx via sigreturn(%p)\n", - (unsigned long long) c->dwarf.ip, sc); - __asm__ __volatile__ ("mov %0, %%esp;" - "mov %1, %%eax;" - "syscall" - :: "r"(sc), "i"(SYS_rt_sigreturn) - : "memory"); - abort(); -} #endif diff --git a/src/native/external/libunwind/src/x86/Gstep.c b/src/native/external/libunwind/src/x86/Gstep.c index 061dcbaaa28097..52e327eded96c1 100644 --- a/src/native/external/libunwind/src/x86/Gstep.c +++ b/src/native/external/libunwind/src/x86/Gstep.c @@ -31,11 +31,24 @@ unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; int ret, i; + int validate = c->validate; + c->validate = 1; Debug (1, "(cursor=%p, ip=0x%08x)\n", c, (unsigned) c->dwarf.ip); + /* + * Special-case the signal trampoline since on many OS targets it lacks DWARF + * unwind info. + */ + if (unw_is_signal_frame (cursor) > 0) + { + ret = x86_handle_signal_frame(cursor); + return 1; + } + /* Try DWARF-based unwinding... */ ret = dwarf_step (&c->dwarf); + c->validate = validate; if (ret < 0 && ret != -UNW_ENOINFO) { @@ -45,54 +58,40 @@ unw_step (unw_cursor_t *cursor) if (unlikely (ret < 0)) { - /* DWARF failed, let's see if we can follow the frame-chain - or skip over the signal trampoline. */ + /* DWARF failed, let's see if we can follow the frame-chain */ struct dwarf_loc ebp_loc, eip_loc, esp_loc; - /* We could get here because of missing/bad unwind information. - Validate all addresses before dereferencing. */ - c->validate = 1; Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret); - if (unw_is_signal_frame (cursor) > 0) + ret = dwarf_get (&c->dwarf, c->dwarf.loc[EBP], &c->dwarf.cfa); + if (ret < 0) { - ret = x86_handle_signal_frame(cursor); - if (ret < 0) - { - Debug (2, "returning 0\n"); - return 0; - } + Debug (2, "returning %d\n", ret); + return ret; } - else - { - ret = dwarf_get (&c->dwarf, c->dwarf.loc[EBP], &c->dwarf.cfa); - if (ret < 0) - { - Debug (2, "returning %d\n", ret); - return ret; - } - - Debug (13, "[EBP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EBP]), - c->dwarf.cfa); - ebp_loc = DWARF_LOC (c->dwarf.cfa, 0); - esp_loc = DWARF_VAL_LOC (c, c->dwarf.cfa + 8); - eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0); - c->dwarf.cfa += 8; + Debug (13, "[EBP=0x%x] = 0x%x\n", DWARF_GET_LOC (c->dwarf.loc[EBP]), c->dwarf.cfa); - /* Mark all registers unsaved, since we don't know where - they are saved (if at all), except for the EBP and - EIP. */ - for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) - c->dwarf.loc[i] = DWARF_NULL_LOC; + ebp_loc = DWARF_LOC (c->dwarf.cfa, 0); + esp_loc = DWARF_VAL_LOC (c, c->dwarf.cfa + 8); + eip_loc = DWARF_LOC (c->dwarf.cfa + 4, 0); + c->dwarf.cfa += 8; - c->dwarf.loc[EBP] = ebp_loc; - c->dwarf.loc[ESP] = esp_loc; - c->dwarf.loc[EIP] = eip_loc; - c->dwarf.use_prev_instr = 1; + /* + * Mark all registers unsaved, since we don't know where they are saved + * (if at all), except for the EBP and EIP. + */ + for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + { + c->dwarf.loc[i] = DWARF_NULL_LOC; } + c->dwarf.loc[EBP] = ebp_loc; + c->dwarf.loc[ESP] = esp_loc; + c->dwarf.loc[EIP] = eip_loc; + c->dwarf.use_prev_instr = 1; + if (!DWARF_IS_NULL_LOC (c->dwarf.loc[EBP])) { ret = dwarf_get (&c->dwarf, c->dwarf.loc[EIP], &c->dwarf.ip); diff --git a/src/native/external/libunwind/src/x86/unwind_i.h b/src/native/external/libunwind/src/x86/unwind_i.h index caa7e02dee40e0..2acc8cbb6be7f3 100644 --- a/src/native/external/libunwind/src/x86/unwind_i.h +++ b/src/native/external/libunwind/src/x86/unwind_i.h @@ -52,7 +52,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define x86_scratch_loc UNW_OBJ(scratch_loc) #define x86_get_scratch_loc UNW_OBJ(get_scratch_loc) #define x86_r_uc_addr UNW_OBJ(r_uc_addr) -#define x86_sigreturn UNW_OBJ(sigreturn) extern void x86_local_addr_space_init (void); extern int x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, @@ -61,7 +60,6 @@ extern dwarf_loc_t x86_scratch_loc (struct cursor *c, unw_regnum_t reg); extern dwarf_loc_t x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg); extern void *x86_r_uc_addr (ucontext_t *uc, int reg); -extern void x86_sigreturn (unw_cursor_t *cursor); #define x86_handle_signal_frame UNW_OBJ(handle_signal_frame) extern int x86_handle_signal_frame(unw_cursor_t *cursor); diff --git a/src/native/external/libunwind/src/x86_64/Gcreate_addr_space.c b/src/native/external/libunwind/src/x86_64/Gcreate_addr_space.c index 9b2db9810abe00..3618d04eba9c86 100644 --- a/src/native/external/libunwind/src/x86_64/Gcreate_addr_space.c +++ b/src/native/external/libunwind/src/x86_64/Gcreate_addr_space.c @@ -30,12 +30,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" -#if defined(_LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN) -#define __LITTLE_ENDIAN _LITTLE_ENDIAN -#endif - unw_addr_space_t -unw_create_addr_space (unw_accessors_t *a, int byte_order) +unw_create_addr_space (unw_accessors_t *a UNUSED, int byte_order UNUSED) { #ifdef UNW_LOCAL_ONLY return NULL; @@ -45,7 +41,7 @@ unw_create_addr_space (unw_accessors_t *a, int byte_order) /* * x86_64 supports only little-endian. */ - if (byte_order != 0 && byte_order != __LITTLE_ENDIAN) + if (byte_order != 0 && byte_order != UNW_LITTLE_ENDIAN) return NULL; as = malloc (sizeof (*as)); diff --git a/src/native/external/libunwind/src/x86_64/Gglobal.c b/src/native/external/libunwind/src/x86_64/Gglobal.c index 812cfb54298e8c..0964d5f3964480 100644 --- a/src/native/external/libunwind/src/x86_64/Gglobal.c +++ b/src/native/external/libunwind/src/x86_64/Gglobal.c @@ -94,8 +94,6 @@ tdep_init (void) dwarf_init (); #ifndef UNW_REMOTE_ONLY - tdep_init_mem_validate (); - x86_64_local_addr_space_init (); #endif atomic_store(&tdep_init_done, 1); /* signal that we're initialized... */ diff --git a/src/native/external/libunwind/src/x86_64/Ginit.c b/src/native/external/libunwind/src/x86_64/Ginit.c index 88146a3e78156c..ca3c0228091391 100644 --- a/src/native/external/libunwind/src/x86_64/Ginit.c +++ b/src/native/external/libunwind/src/x86_64/Ginit.c @@ -54,14 +54,14 @@ static struct unw_addr_space local_addr_space; unw_addr_space_t unw_local_addr_space = &local_addr_space; static void -put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) +put_unwind_info (unw_addr_space_t as UNUSED, unw_proc_info_t *proc_info UNUSED, void *arg UNUSED) { /* it's a no-op */ } static int -get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, - void *arg) +get_dyn_info_list_addr (unw_addr_space_t as UNUSED, unw_word_t *dyn_info_list_addr, + void *arg UNUSED) { #ifndef UNW_LOCAL_ONLY # pragma weak _U_dyn_info_list_addr @@ -73,260 +73,32 @@ get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, return 0; } -static int mem_validate_pipe[2] = {-1, -1}; -#ifdef HAVE_PIPE2 -static inline void -do_pipe2 (int pipefd[2]) -{ - int result UNUSED = pipe2 (pipefd, O_CLOEXEC | O_NONBLOCK); -} -#else -static inline void -set_pipe_flags (int fd) -{ - int fd_flags = fcntl (fd, F_GETFD, 0); - int status_flags = fcntl (fd, F_GETFL, 0); - - fd_flags |= FD_CLOEXEC; - fcntl (fd, F_SETFD, fd_flags); - - status_flags |= O_NONBLOCK; - fcntl (fd, F_SETFL, status_flags); -} - -static inline void -do_pipe2 (int pipefd[2]) -{ - pipe (pipefd); - set_pipe_flags(pipefd[0]); - set_pipe_flags(pipefd[1]); -} -#endif - -static inline void -open_pipe (void) -{ - if (mem_validate_pipe[0] != -1) - close (mem_validate_pipe[0]); - if (mem_validate_pipe[1] != -1) - close (mem_validate_pipe[1]); - - do_pipe2 (mem_validate_pipe); -} - -ALWAYS_INLINE static int -write_validate (void *addr) -{ - int ret = -1; - ssize_t bytes = 0; - - do - { - char buf; - bytes = read (mem_validate_pipe[0], &buf, 1); - } - while ( errno == EINTR ); - - int valid_read = (bytes > 0 || errno == EAGAIN || errno == EWOULDBLOCK); - if (!valid_read) - { - // re-open closed pipe - open_pipe (); - } - - do - { -#ifdef HAVE_SYS_SYSCALL_H - /* use syscall insteadof write() so that ASAN does not complain */ - ret = syscall (SYS_write, mem_validate_pipe[1], addr, 1); -#else - ret = write (mem_validate_pipe[1], addr, 1); -#endif - } - while ( errno == EINTR ); - - return ret; -} - -static int (*mem_validate_func) (void *addr, size_t len); -static int msync_validate (void *addr, size_t len) -{ - if (msync (addr, len, MS_ASYNC) != 0) - { - return -1; - } - - return write_validate (addr); -} - -#ifdef HAVE_MINCORE -static int mincore_validate (void *addr, size_t len) -{ - unsigned char mvec[2]; /* Unaligned access may cross page boundary */ - - /* mincore could fail with EAGAIN but we conservatively return -1 - instead of looping. */ - if (mincore (addr, len, (unsigned char *)mvec) != 0) - { - return -1; - } - - return write_validate (addr); -} -#endif - -/* Initialise memory validation method. On linux kernels <2.6.21, - mincore() returns incorrect value for MAP_PRIVATE mappings, - such as stacks. If mincore() was available at compile time, - check if we can actually use it. If not, use msync() instead. */ -HIDDEN void -tdep_init_mem_validate (void) -{ - open_pipe (); - -#ifdef HAVE_MINCORE - unsigned char present = 1; - size_t len = unw_page_size; - unw_word_t addr = uwn_page_start((unw_word_t)&present); - unsigned char mvec[1]; - int ret; - while ((ret = mincore ((void*)addr, len, (unsigned char *)mvec)) == -1 && - errno == EAGAIN) {} - if (ret == 0) - { - Debug(1, "using mincore to validate memory\n"); - mem_validate_func = mincore_validate; - } - else -#endif - { - Debug(1, "using msync to validate memory\n"); - mem_validate_func = msync_validate; - } -} - -/* Cache of already validated addresses */ -#define NLGA 4 -#if defined(HAVE___CACHE_PER_THREAD) && HAVE___CACHE_PER_THREAD -// thread-local variant -static _Thread_local unw_word_t last_good_addr[NLGA]; -static _Thread_local int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == last_good_addr[i]) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (last_good_addr[victim] == 0) { - last_good_addr[victim] = addr; - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; -} - -#else -// global, thread safe variant -static _Atomic unw_word_t last_good_addr[NLGA]; -static _Atomic int lga_victim; - -static int -is_cached_valid_mem(unw_word_t addr) -{ - int i; - for (i = 0; i < NLGA; i++) - { - if (addr == atomic_load(&last_good_addr[i])) - return 1; - } - return 0; -} - -static void -cache_valid_mem(unw_word_t addr) -{ - int i, victim; - victim = atomic_load(&lga_victim); - unw_word_t zero = 0; - for (i = 0; i < NLGA; i++) { - if (atomic_compare_exchange_strong(&last_good_addr[victim], &zero, addr)) { - return; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - atomic_store(&last_good_addr[victim], addr); - victim = (victim + 1) % NLGA; - atomic_store(&lga_victim, victim); -} -#endif - -static int -validate_mem (unw_word_t addr) -{ - size_t len = unw_page_size; - addr = uwn_page_start(addr); - - if (addr == 0) - return -1; - - if (is_cached_valid_mem(addr)) - return 0; - - if (mem_validate_func ((void *) addr, len) == -1) - return -1; - - cache_valid_mem(addr); - - return 0; -} - -static int -access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, +access_mem (unw_addr_space_t as UNUSED, unw_word_t addr, unw_word_t *val, int write, void *arg) { if (unlikely (write)) { Debug (16, "mem[%016lx] <- %lx\n", addr, *val); - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); } else { /* validate address */ if (unlikely (AS_ARG_GET_VALIDATE(arg)) - && unlikely (validate_mem (addr))) { + && unlikely (!unw_address_is_valid (addr, sizeof (unw_word_t)))) { Debug (16, "mem[%016lx] -> invalid\n", addr); return -1; } - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (16, "mem[%016lx] -> %lx\n", addr, *val); } return 0; } static int -access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, +access_reg (unw_addr_space_t as UNUSED, unw_regnum_t reg, unw_word_t *val, int write, void *arg) { unw_word_t *addr; @@ -340,12 +112,12 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, if (write) { - *(unw_word_t *) addr = *val; + memcpy ((void *) addr, val, sizeof(unw_word_t)); Debug (12, "%s <- 0x%016lx\n", unw_regname (reg), *val); } else { - *val = *(unw_word_t *) addr; + memcpy (val, (void *) addr, sizeof(unw_word_t)); Debug (12, "%s -> 0x%016lx\n", unw_regname (reg), *val); } return 0; @@ -356,7 +128,7 @@ access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write, } static int -access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, +access_fpreg (unw_addr_space_t as UNUSED, unw_regnum_t reg, unw_fpreg_t *val, int write, void *arg) { ucontext_t *uc = AS_ARG_GET_UC_PTR(arg); @@ -391,15 +163,28 @@ access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val, static int get_static_proc_name (unw_addr_space_t as, unw_word_t ip, char *buf, size_t buf_len, unw_word_t *offp, - void *arg) + void *arg UNUSED) { return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp); } +static int +get_static_elf_filename (unw_addr_space_t as, unw_word_t ip, + char *buf, size_t buf_len, unw_word_t *offp, + void *arg UNUSED) +{ + return _Uelf64_get_elf_filename (as, getpid (), ip, buf, buf_len, offp); +} + HIDDEN void x86_64_local_addr_space_init (void) { memset (&local_addr_space, 0, sizeof (local_addr_space)); +#ifndef UNW_REMOTE_ONLY +# if defined(HAVE_DL_ITERATE_PHDR) + local_addr_space.iterate_phdr_function = dl_iterate_phdr; +# endif +#endif local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY; local_addr_space.acc.find_proc_info = dwarf_find_proc_info; local_addr_space.acc.put_unwind_info = put_unwind_info; @@ -409,6 +194,7 @@ x86_64_local_addr_space_init (void) local_addr_space.acc.access_fpreg = access_fpreg; local_addr_space.acc.resume = x86_64_local_resume; local_addr_space.acc.get_proc_name = get_static_proc_name; + local_addr_space.acc.get_elf_filename = get_static_elf_filename; unw_flush_cache (&local_addr_space, 0, 0); } diff --git a/src/native/external/libunwind/src/x86_64/Ginit_remote.c b/src/native/external/libunwind/src/x86_64/Ginit_remote.c index 51761a7102f925..1675aeb5f1561d 100644 --- a/src/native/external/libunwind/src/x86_64/Ginit_remote.c +++ b/src/native/external/libunwind/src/x86_64/Ginit_remote.c @@ -30,7 +30,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "unwind_i.h" int -unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) +unw_init_remote (unw_cursor_t *cursor UNUSED, unw_addr_space_t as UNUSED, void *as_arg UNUSED) { #ifdef UNW_LOCAL_ONLY return -UNW_EINVAL; diff --git a/src/native/external/libunwind/src/x86_64/Gos-freebsd.c b/src/native/external/libunwind/src/x86_64/Gos-freebsd.c index e8f04381679f08..59706b977b812e 100644 --- a/src/native/external/libunwind/src/x86_64/Gos-freebsd.c +++ b/src/native/external/libunwind/src/x86_64/Gos-freebsd.c @@ -38,7 +38,7 @@ unw_is_signal_frame (unw_cursor_t *cursor) { /* XXXKIB */ struct cursor *c = (struct cursor *) cursor; - unw_word_t w0, w1, w2, b0, ip; + unw_word_t w0, w1, w2, ip; unw_addr_space_t as; unw_accessors_t *a; void *arg; @@ -69,22 +69,9 @@ eb fd jmp 0b w2 == 0x0000000000fdebf4) { c->sigcontext_format = X86_64_SCF_FREEBSD_SIGFRAME; - return (c->sigcontext_format); + return (1); } - /* Check if RIP points at standard syscall sequence. -49 89 ca mov %rcx,%r10 -0f 05 syscall - */ - if ((ret = (*a->access_mem) (as, ip - 5, &b0, 0, arg)) < 0) - return (0); - Debug (12, "b0 0x%lx\n", b0); - if ((b0 & 0xffffffffffffff) == 0x050fca89490000 || - (b0 & 0xffffffffff) == 0x050fca8949) - { - c->sigcontext_format = X86_64_SCF_FREEBSD_SYSCALL; - return (c->sigcontext_format); - } - return (X86_64_SCF_NONE); + return (0); } HIDDEN int @@ -131,26 +118,6 @@ x86_64_handle_signal_frame (unw_cursor_t *cursor) return 0; } - else if (c->sigcontext_format == X86_64_SCF_FREEBSD_SYSCALL) - { - c->dwarf.loc[RCX] = c->dwarf.loc[R10]; - /* rsp_loc = DWARF_LOC(c->dwarf.cfa - 8, 0); */ - /* rbp_loc = c->dwarf.loc[RBP]; */ - c->dwarf.loc[RSP] = DWARF_VAL_LOC (c, c->dwarf.cfa + 8); - c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); - ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip); - Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n", - (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]), - (unsigned long long) c->dwarf.ip); - if (ret < 0) - { - Debug (2, "returning %d\n", ret); - return ret; - } - c->dwarf.cfa += 8; - c->dwarf.use_prev_instr = 1; - return 1; - } else return -UNW_EBADFRAME; @@ -220,3 +187,50 @@ x86_64_sigreturn (unw_cursor_t *cursor) abort(); } #endif + +HIDDEN int +x86_64_os_step(struct cursor *c) +{ + unw_word_t b0, ip; + unw_addr_space_t as; + unw_accessors_t *a; + void *arg; + int ret; + + as = c->dwarf.as; + a = unw_get_accessors_int (as); + arg = c->dwarf.as_arg; + ip = c->dwarf.ip; + + /* + * Check if RIP points at standard syscall sequence. + * 49 89 ca mov %rcx,%r10 + * 0f 05 syscall + */ + if ((ret = (*a->access_mem) (as, ip - 5, &b0, 0, arg)) < 0) + return (0); + Debug (12, "b0 0x%lx\n", b0); + if ((b0 & 0xffffffffffffff) == 0x050fca89490000 || + (b0 & 0xffffffffff) == 0x050fca8949) + { + c->sigcontext_format = X86_64_SCF_FREEBSD_SYSCALL; + c->dwarf.loc[RCX] = c->dwarf.loc[R10]; + /* rsp_loc = DWARF_LOC(c->dwarf.cfa - 8, 0); */ + /* rbp_loc = c->dwarf.loc[RBP]; */ + c->dwarf.loc[RSP] = DWARF_VAL_LOC (c, c->dwarf.cfa + 8); + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip); + Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n", + (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]), + (unsigned long long) c->dwarf.ip); + if (ret < 0) + { + Debug (2, "returning %d\n", ret); + return ret; + } + c->dwarf.cfa += 8; + c->dwarf.use_prev_instr = 1; + return 1; + } + return (0); +} diff --git a/src/native/external/libunwind/src/x86_64/Gos-linux.c b/src/native/external/libunwind/src/x86_64/Gos-linux.c index b4893297b16fbc..c10ce6eed2edf1 100644 --- a/src/native/external/libunwind/src/x86_64/Gos-linux.c +++ b/src/native/external/libunwind/src/x86_64/Gos-linux.c @@ -25,6 +25,10 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifdef HAVE_ASM_VSYSCALL_H +#include +#endif + #include "libunwind_i.h" #include "unwind_i.h" #include "ucontext_i.h" @@ -32,7 +36,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include HIDDEN void -tdep_fetch_frame (struct dwarf_cursor *dw, unw_word_t ip, int need_unwind_info) +tdep_fetch_frame (struct dwarf_cursor *dw, unw_word_t ip UNUSED, int need_unwind_info UNUSED) { struct cursor *c = (struct cursor *) dw; assert(! need_unwind_info || dw->pi_valid); @@ -85,7 +89,7 @@ unw_is_signal_frame (unw_cursor_t *cursor) } HIDDEN int -x86_64_handle_signal_frame (unw_cursor_t *cursor) +x86_64_handle_signal_frame (unw_cursor_t *cursor UNUSED) { #if UNW_DEBUG /* To silence compiler warnings */ /* Should not get here because we now use kernel-provided dwarf @@ -155,3 +159,33 @@ x86_64_sigreturn (unw_cursor_t *cursor) } #endif + +static int +is_vsyscall (struct dwarf_cursor *c UNUSED) +{ +#if defined(VSYSCALL_START) && defined(VSYSCALL_END) + return c->ip >= VSYSCALL_START && c->ip < VSYSCALL_END; +#elif defined(VSYSCALL_ADDR) + /* Linux 3.16 removes `VSYSCALL_START` and `VSYSCALL_END`. Assume + a single page is mapped for vsyscalls. */ + return c->ip >= VSYSCALL_ADDR && c->ip < VSYSCALL_ADDR + unw_page_size; +#else + return 0; +#endif +} + +HIDDEN int +x86_64_os_step(struct cursor *c) +{ + if (is_vsyscall (&c->dwarf)) + { + Debug (2, "in vsyscall region\n"); + c->frame_info.cfa_reg_offset = 8; + c->frame_info.cfa_reg_rsp = -1; + c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; + c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); + c->dwarf.cfa += 8; + return (1); + } + return (0); +} diff --git a/src/native/external/libunwind/src/x86_64/Gos-qnx.c b/src/native/external/libunwind/src/x86_64/Gos-qnx.c new file mode 100644 index 00000000000000..be4e187a113495 --- /dev/null +++ b/src/native/external/libunwind/src/x86_64/Gos-qnx.c @@ -0,0 +1,214 @@ +/* + * Copyright 2022, 2023 BlackBerry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "config.h" + +#include "unwind_i.h" +#include "ucontext_i.h" +#include + + +/** + * @brief Predicate to check if current IP is a signal trampoline. + * @param[in] cursor The current unwinding state. + * + * This function assumes the IP points to the first instruction after the call + * to the signal handler, and the bytes checked are the opcodes dumped by + * objdump between the call and the syscall that returns from the signal. + * + * This is not 100% robust but it's unlikely any other syscall setup is + * identical. + * + * @returns 0 if it does not detect the current function is a signal trampoline, + * 1 if it detects the trampoline. + */ +int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + const unsigned char sig[] = + { + 0x4c, 0x89, 0xef, // mov %r13,%rdi + 0x49, 0x8b, 0x74, 0x24, 0x30, // mov 0x30(%r12),%rsi + 0x49, 0x8b, 0x54, 0x24, 0x38, // mov 0x38(%r12),%rdx + 0x4d, 0x8b, 0x44, 0x24, 0x48, // mov 0x48(%r12),%r8 + 0x4d, 0x8b, 0x4c, 0x24, 0x50, // mov 0x50(%r12),%r9 + 0x49, 0x8b, 0x5c, 0x24, 0x60, // mov 0x60(%r12),%rbx + 0x49, 0x8b, 0x6c, 0x24, 0x68, // mov 0x68(%r12),%rbp + 0x4d, 0x8b, 0xac, 0x24, 0x88, 0x00, 0x00, 0x00, // mov 0x88(%r12),%r13 + 0x4d, 0x8b, 0xb4, 0x24, 0x90, 0x00, 0x00, 0x00, // mov 0x90(%r12),%r14 + 0x4d, 0x8b, 0xbc, 0x24, 0x98, 0x00, 0x00, 0x00, // mov 0x98(%r12),%r15 + 0x4d, 0x8b, 0xa4, 0x24, 0x80, 0x00, 0x00, 0x00, // mov 0x80(%r12),%r12 + 0x48, 0xc7, 0xc0, 0x1b, 0x00, 0x00, 0x00, // mov $0x1b,%rax + 0x0f, 0x05, // syscall + }; + + struct cursor *c = (struct cursor *) cursor; + unw_addr_space_t as = c->dwarf.as; + unw_accessors_t *a = unw_get_accessors_int (as); + unw_word_t ip = c->dwarf.ip; + int retval = 1; + +#if CONSERVATIVE_CHECKS + int val = 0; + if (c->dwarf.as == unw_local_addr_space) { + val = dwarf_get_validate(&c->dwarf); + dwarf_set_validate(&c->dwarf, 1); + } +#endif + + unw_word_t w = 0; + for (size_t i = 0; i != sizeof(sig)/sizeof(sig[0]); ++i) + { + size_t byte_index = i % sizeof(unw_word_t); + if (0 == byte_index) + { + int ret = a->access_mem (as, ip, &w, 0, c->dwarf.as_arg); + if (ret < 0) + { + retval = 0; + break; + } + ip += sizeof(w); + } + + if (sig[i] != (w&0xff)) + { + retval = 0; + break; + } + w >>= 8; + } + +#if CONSERVATIVE_CHECKS + if (c->dwarf.as == unw_local_addr_space) { + dwarf_set_validate(&c->dwarf, val); + } +#endif + + return retval; +} + + +/** + * @brief Special handling when a signal trampoline is detected + * @param[in] cursor The current unwinding state. + * + * If this is a signal trampoline then %rsp points directly at the kernel + * sighandler info so it's easy to get the ucontext. + * + * @returns < 0 on failure, 0 on success. + */ +HIDDEN int +x86_64_handle_signal_frame (unw_cursor_t *cursor) +{ + struct cursor * c = (struct cursor *) cursor; + unw_addr_space_t as = c->dwarf.as; + unw_accessors_t * a = unw_get_accessors_int (as); + unw_word_t sp = c->dwarf.cfa; + unw_word_t uc_ptr_addr = sp + offsetof(struct _sighandler_info, context); + + ucontext_t *context = NULL; + int ret = a->access_mem (as, uc_ptr_addr, (unw_word_t*)&context, 0, c->dwarf.as_arg); + if (ret < 0) + { + return -UNW_EBADFRAME; + } + Debug(3, "unwind context at %#010lx\n", (long)context); + + for (size_t i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) + { + c->dwarf.loc[i] = DWARF_NULL_LOC; + } + + c->dwarf.loc[RAX] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rax); + c->dwarf.loc[RDX] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rdx); + c->dwarf.loc[RCX] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rcx); + c->dwarf.loc[RBX] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rbx); + c->dwarf.loc[RSI] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rsi); + c->dwarf.loc[RDI] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rdi); + c->dwarf.loc[RBP] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rbp); + c->dwarf.loc[RSP] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rsp); + c->dwarf.loc[ R8] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r8); + c->dwarf.loc[ R9] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r9); + c->dwarf.loc[R10] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r10); + c->dwarf.loc[R11] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r11); + c->dwarf.loc[R12] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r12); + c->dwarf.loc[R13] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r13); + c->dwarf.loc[R14] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r14); + c->dwarf.loc[R15] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.r15); + c->dwarf.loc[RIP] = DWARF_VAL_LOC (&c->dwarf, context->uc_mcontext.cpu.rip); + + dwarf_get (&c->dwarf, c->dwarf.loc[RSP], &c->dwarf.cfa); + dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip); + + return 0; +} + + +#ifndef UNW_REMOTE_ONLY +HIDDEN void * +x86_64_r_uc_addr (ucontext_t *uc, int reg) +{ + void *addr; + + switch (reg) + { + case UNW_X86_64_R8: addr = &uc->uc_mcontext.cpu.r8; break; + case UNW_X86_64_R9: addr = &uc->uc_mcontext.cpu.r9; break; + case UNW_X86_64_R10: addr = &uc->uc_mcontext.cpu.r10; break; + case UNW_X86_64_R11: addr = &uc->uc_mcontext.cpu.r11; break; + case UNW_X86_64_R12: addr = &uc->uc_mcontext.cpu.r12; break; + case UNW_X86_64_R13: addr = &uc->uc_mcontext.cpu.r13; break; + case UNW_X86_64_R14: addr = &uc->uc_mcontext.cpu.r14; break; + case UNW_X86_64_R15: addr = &uc->uc_mcontext.cpu.r15; break; + case UNW_X86_64_RDI: addr = &uc->uc_mcontext.cpu.rdi; break; + case UNW_X86_64_RSI: addr = &uc->uc_mcontext.cpu.rsi; break; + case UNW_X86_64_RBP: addr = &uc->uc_mcontext.cpu.rbp; break; + case UNW_X86_64_RBX: addr = &uc->uc_mcontext.cpu.rbx; break; + case UNW_X86_64_RDX: addr = &uc->uc_mcontext.cpu.rdx; break; + case UNW_X86_64_RAX: addr = &uc->uc_mcontext.cpu.rax; break; + case UNW_X86_64_RCX: addr = &uc->uc_mcontext.cpu.rcx; break; + case UNW_X86_64_RSP: addr = &uc->uc_mcontext.cpu.rsp; break; + case UNW_X86_64_RIP: addr = &uc->uc_mcontext.cpu.rip; break; + + default: + addr = NULL; + } + return addr; +} + +HIDDEN NORETURN void +x86_64_sigreturn (unw_cursor_t *cursor) +{ + Debug(0, "Unsupported function.\n"); + abort(); +} + +#endif + +HIDDEN int +x86_64_os_step(struct cursor *c) +{ + return (0); +} diff --git a/src/native/external/libunwind/src/x86_64/Gos-solaris.c b/src/native/external/libunwind/src/x86_64/Gos-solaris.c index a9957ba8c9d762..d570d08e101164 100644 --- a/src/native/external/libunwind/src/x86_64/Gos-solaris.c +++ b/src/native/external/libunwind/src/x86_64/Gos-solaris.c @@ -135,3 +135,9 @@ x86_64_sigreturn (unw_cursor_t *cursor) } #endif + +HIDDEN int +x86_64_os_step(struct cursor *c) +{ + return (0); +} diff --git a/src/native/external/libunwind/src/x86_64/Gregs.c b/src/native/external/libunwind/src/x86_64/Gregs.c index dff5bcbe74b6e0..88c466b9ec612c 100644 --- a/src/native/external/libunwind/src/x86_64/Gregs.c +++ b/src/native/external/libunwind/src/x86_64/Gregs.c @@ -131,8 +131,8 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp, } HIDDEN int -tdep_access_fpreg (struct cursor *c, unw_regnum_t reg, unw_fpreg_t *valp, - int write) +tdep_access_fpreg (struct cursor *c UNUSED, unw_regnum_t reg UNUSED, + unw_fpreg_t *valp UNUSED, int write UNUSED) { return -UNW_EBADREG; } diff --git a/src/native/external/libunwind/src/x86_64/Gresume.c b/src/native/external/libunwind/src/x86_64/Gresume.c index becb1bd6cd7690..572e048953a509 100644 --- a/src/native/external/libunwind/src/x86_64/Gresume.c +++ b/src/native/external/libunwind/src/x86_64/Gresume.c @@ -28,13 +28,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "libunwind_i.h" -#include "offsets.h" #include "unwind_i.h" #ifndef UNW_REMOTE_ONLY HIDDEN inline int -x86_64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) +x86_64_local_resume (unw_addr_space_t as UNUSED, unw_cursor_t *cursor, void *arg UNUSED) { struct cursor *c = (struct cursor *) cursor; ucontext_t *uc = dwarf_get_uc(&c->dwarf); diff --git a/src/native/external/libunwind/src/x86_64/Gstep.c b/src/native/external/libunwind/src/x86_64/Gstep.c index 146c27ee49f92d..72ebbd9643cfbd 100644 --- a/src/native/external/libunwind/src/x86_64/Gstep.c +++ b/src/native/external/libunwind/src/x86_64/Gstep.c @@ -25,10 +25,6 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifdef HAVE_ASM_VSYSCALL_H -#include -#endif - #include "libunwind_i.h" #include "unwind_i.h" #include @@ -57,20 +53,6 @@ is_plt_entry (struct dwarf_cursor *c) return ret; } -static int -is_vsyscall (struct dwarf_cursor *c) -{ -#if defined(VSYSCALL_START) && defined(VSYSCALL_END) - return c->ip >= VSYSCALL_START && c->ip < VSYSCALL_END; -#elif defined(VSYSCALL_ADDR) - /* Linux 3.16 removes `VSYSCALL_START` and `VSYSCALL_END`. Assume - a single page is mapped for vsyscalls. */ - return c->ip >= VSYSCALL_ADDR && c->ip < VSYSCALL_ADDR + sysconf(_SC_PAGESIZE); -#else - return 0; -#endif -} - int unw_step (unw_cursor_t *cursor) { @@ -140,7 +122,15 @@ unw_step (unw_cursor_t *cursor) Debug (13, "dwarf_step() failed (ret=%d), trying frame-chain\n", ret); - if (unw_is_signal_frame (cursor) > 0) + if ((ret = x86_64_os_step (c)) != 0) + { + if (ret < 0) + { + Debug (2, "returning 0\n"); + return 0; + } + } + else if (unw_is_signal_frame (cursor) > 0) { ret = x86_64_handle_signal_frame(cursor); if (ret < 0) @@ -159,15 +149,6 @@ unw_step (unw_cursor_t *cursor) c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); c->dwarf.cfa += 8; } - else if (is_vsyscall (&c->dwarf)) - { - Debug (2, "in vsyscall region\n"); - c->frame_info.cfa_reg_offset = 8; - c->frame_info.cfa_reg_rsp = -1; - c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; - c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); - c->dwarf.cfa += 8; - } else if (DWARF_IS_NULL_LOC (c->dwarf.loc[RBP])) { for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i) @@ -231,15 +212,19 @@ unw_step (unw_cursor_t *cursor) */ c->dwarf.cfa += 8; /* Optimised x64 binaries don't use RBP it seems? */ - rbp_loc = DWARF_LOC (rbp, 0); - rsp_loc = DWARF_LOC (rsp, 0); + rbp_loc = c->dwarf.loc[RBP]; + rsp_loc = DWARF_VAL_LOC (c, rsp + 8); rip_loc = DWARF_LOC (rsp, 0); } else - Debug (2, "new_ip 0x%lx dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, new_ip), ¬_used) != 0\n", new_ip); + { + Debug (2, "new_ip 0x%lx dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, new_ip), ¬_used) != 0\n", new_ip); + } } - else + else + { Debug (2, "rsp 0x%lx dwarf_get(&c->dwarf, DWARF_MEM_LOC(c->dwarf, rsp), &new_ip) != 0\n", rsp); + } } /* * If the previous rip we found on the stack didn't look valid fall back diff --git a/src/native/external/libunwind/src/x86_64/Gtrace.c b/src/native/external/libunwind/src/x86_64/Gtrace.c index 7f8964199b61bd..491085165fdda2 100644 --- a/src/native/external/libunwind/src/x86_64/Gtrace.c +++ b/src/native/external/libunwind/src/x86_64/Gtrace.c @@ -69,7 +69,7 @@ trace_cache_free (void *arg) } tls_cache_destroyed = 1; tls_cache = NULL; - munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t)); + mi_munmap (cache->frames, (1u << cache->log_size) * sizeof(unw_tdep_frame_t)); mempool_free (&trace_cache_pool, cache); Debug(5, "freed cache %p\n", cache); } @@ -151,7 +151,7 @@ trace_cache_expand (unw_trace_cache_t *cache) } Debug(5, "expanded cache from 2^%lu to 2^%lu buckets\n", cache->log_size, new_log_size); - munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t)); + mi_munmap(cache->frames, old_size * sizeof(unw_tdep_frame_t)); cache->frames = new_frames; cache->log_size = new_log_size; cache->used = 0; @@ -406,7 +406,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) int ret; int validate UNUSED = 0; - /* Check input parametres. */ + /* Check input parameters. */ if (unlikely(! cursor || ! buffer || ! size || (maxdepth = *size) <= 0)) return -UNW_EINVAL; @@ -477,6 +477,7 @@ tdep_trace (unw_cursor_t *cursor, void **buffer, int *size) /* Fall thru to standard processing after forcing validation. */ if (d->as == unw_local_addr_space) dwarf_set_validate(d, 1); + FALLTHROUGH; case UNW_X86_64_FRAME_STANDARD: /* Advance standard traceable frame. */ diff --git a/src/native/external/libunwind/src/x86_64/Los-qnx.c b/src/native/external/libunwind/src/x86_64/Los-qnx.c new file mode 100644 index 00000000000000..05c771bb944214 --- /dev/null +++ b/src/native/external/libunwind/src/x86_64/Los-qnx.c @@ -0,0 +1,29 @@ +/* + * Copyright 2022 BlackBerry Limited. + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#define UNW_LOCAL_ONLY +#include +#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY) +#include "Gos-qnx.c" +#endif diff --git a/src/native/external/libunwind/src/x86_64/getcontext.S b/src/native/external/libunwind/src/x86_64/getcontext.S index e1450719b7da1a..a08fccd6c32288 100644 --- a/src/native/external/libunwind/src/x86_64/getcontext.S +++ b/src/native/external/libunwind/src/x86_64/getcontext.S @@ -88,6 +88,7 @@ _Ux86_64_getcontext: movw %gs, UC_MCONTEXT_GS(%rdi) #endif movq $UC_MCONTEXT_MC_LEN_VAL, UC_MCONTEXT_MC_LEN(%rdi) +#elif defined(__QNX__) #else #error Port me #endif diff --git a/src/native/external/libunwind/src/x86_64/is_fpreg.c b/src/native/external/libunwind/src/x86_64/is_fpreg.c index 5c036137b630fc..8edf6d3a444ed1 100644 --- a/src/native/external/libunwind/src/x86_64/is_fpreg.c +++ b/src/native/external/libunwind/src/x86_64/is_fpreg.c @@ -28,7 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "libunwind_i.h" int -unw_is_fpreg (int regnum) +unw_is_fpreg (int regnum UNUSED) { #if 0 return ((regnum >= UNW_X86_ST0 && regnum <= UNW_X86_ST7) diff --git a/src/native/external/libunwind/src/x86_64/offsets.h b/src/native/external/libunwind/src/x86_64/offsets.h deleted file mode 100644 index 0807960f30c0c9..00000000000000 --- a/src/native/external/libunwind/src/x86_64/offsets.h +++ /dev/null @@ -1,3 +0,0 @@ -/* FreeBSD specific definitions */ - -#define FREEBSD_UC_MCONTEXT_OFF 0x10 diff --git a/src/native/external/libunwind/src/x86_64/setcontext.S b/src/native/external/libunwind/src/x86_64/setcontext.S index 17e5ae12032aad..17c4b94ba23b1d 100644 --- a/src/native/external/libunwind/src/x86_64/setcontext.S +++ b/src/native/external/libunwind/src/x86_64/setcontext.S @@ -54,6 +54,7 @@ _Ux86_64_setcontext: jne 1f fxrstor UC_MCONTEXT_FPSTATE(%rdi) 1: +#elif defined (__QNX__) #else #error Port me #endif diff --git a/src/native/external/libunwind/src/x86_64/siglongjmp.S b/src/native/external/libunwind/src/x86_64/siglongjmp.S index 32489e53a9f19c..c75d027c09a7cb 100644 --- a/src/native/external/libunwind/src/x86_64/siglongjmp.S +++ b/src/native/external/libunwind/src/x86_64/siglongjmp.S @@ -23,10 +23,28 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#if defined(__FreeBSD__) +#define SIG_SETMASK 3 +#endif + .globl _UI_siglongjmp_cont .type _UI_siglongjmp_cont, @function _UI_siglongjmp_cont: +#if defined(__linux__) || defined(__QNX__) retq +#elif defined(__FreeBSD__) + pushq %rax /* return address */ + pushq %rdx /* return value */ + movq $SIG_SETMASK,%rdi + movq %rcx,%rsi + xorq %rdx,%rdx + movq $340,%rax + syscall + popq %rax /* return value */ + retq /* return address on stack */ +#else +#error Port me +#endif .size _UI_siglongjmp_cont, . - _UI_siglongjmp_cont /* We do not need executable stack. */ .section .note.GNU-stack,"",@progbits diff --git a/src/native/external/libunwind/src/x86_64/ucontext_i.h b/src/native/external/libunwind/src/x86_64/ucontext_i.h index e886c948453ceb..a4c0d340239825 100644 --- a/src/native/external/libunwind/src/x86_64/ucontext_i.h +++ b/src/native/external/libunwind/src/x86_64/ucontext_i.h @@ -78,6 +78,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define UC_MCONTEXT_FPOWNED_FPU 0x20001 #define UC_MCONTEXT_FPFMT_XMM 0x10002 #define UC_MCONTEXT_MC_LEN_VAL 0x320 +#elif defined(__QNX__) +#define UC_SIGMASK 0x0 +#define UC_MCONTEXT_GREGS_R8 0x48 +#define UC_MCONTEXT_GREGS_R9 0x50 +#define UC_MCONTEXT_GREGS_R10 0x40 +#define UC_MCONTEXT_GREGS_R11 0x78 +#define UC_MCONTEXT_GREGS_R12 0x80 +#define UC_MCONTEXT_GREGS_R13 0x88 +#define UC_MCONTEXT_GREGS_R14 0x90 +#define UC_MCONTEXT_GREGS_R15 0x98 +#define UC_MCONTEXT_GREGS_RDI 0x28 +#define UC_MCONTEXT_GREGS_RSI 0x30 +#define UC_MCONTEXT_GREGS_RBP 0x68 +#define UC_MCONTEXT_GREGS_RBX 0x60 +#define UC_MCONTEXT_GREGS_RDX 0x38 +#define UC_MCONTEXT_GREGS_RAX 0x58 +#define UC_MCONTEXT_GREGS_RCX 0x70 +#define UC_MCONTEXT_GREGS_RSP 0xb8 +#define UC_MCONTEXT_GREGS_RIP 0xa0 #elif defined __sun #define UC_MCONTEXT_GREGS_R8 0x78 #define UC_MCONTEXT_GREGS_R9 0x70 diff --git a/src/native/external/libunwind/src/x86_64/unwind_i.h b/src/native/external/libunwind/src/x86_64/unwind_i.h index 49fa078fb21cf2..a20c22f99d37a7 100644 --- a/src/native/external/libunwind/src/x86_64/unwind_i.h +++ b/src/native/external/libunwind/src/x86_64/unwind_i.h @@ -89,5 +89,7 @@ extern void *x86_64_r_uc_addr (ucontext_t *uc, int reg); extern NORETURN void x86_64_sigreturn (unw_cursor_t *cursor); #define x86_64_handle_signal_frame UNW_OBJ(handle_signal_frame) extern int x86_64_handle_signal_frame(unw_cursor_t *cursor); +#define x86_64_os_step UNW_OBJ(os_step) +extern HIDDEN int x86_64_os_step(struct cursor *c); #endif /* unwind_i_h */ diff --git a/src/native/external/libunwind/tests/Gperf-simple.c b/src/native/external/libunwind/tests/Gperf-simple.c index e1819182140349..20406050348111 100644 --- a/src/native/external/libunwind/tests/Gperf-simple.c +++ b/src/native/external/libunwind/tests/Gperf-simple.c @@ -38,7 +38,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ long dummy; static long iterations = 10000; -static int maxlevel = 100; #define KB 1024 #define MB (1024*1024) @@ -110,7 +109,7 @@ f1 (int level, int maxlevel, double *step) } static void -doit (const char *label) +doit (const char *label, int maxlevel) { double step, min_step, first_step, sum_step; int i; @@ -232,10 +231,11 @@ measure_init (void) int main (int argc, char **argv) { - struct rlimit rlim; - - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; + int maxlevel = 100; + struct rlimit rlim = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY + }; setrlimit (RLIMIT_STACK, &rlim); memset (big, 0xaa, sizeof (big)); @@ -244,21 +244,21 @@ main (int argc, char **argv) { maxlevel = atol (argv[1]); if (argc > 2) - iterations = atol (argv[2]); + iterations = atol (argv[2]); } measure_init (); - doit ("default "); + doit ("default ", maxlevel); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE); - doit ("no cache "); + doit ("no cache ", maxlevel); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL); - doit ("global cache "); + doit ("global cache ", maxlevel); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD); - doit ("per-thread cache"); + doit ("per-thread cache", maxlevel); return 0; } diff --git a/src/native/external/libunwind/tests/Gperf-trace.c b/src/native/external/libunwind/tests/Gperf-trace.c index 4d24fa5ca86574..51ee5ee69f6d98 100644 --- a/src/native/external/libunwind/tests/Gperf-trace.c +++ b/src/native/external/libunwind/tests/Gperf-trace.c @@ -32,13 +32,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#define panic(args...) \ - do { fprintf (stderr, args); exit (-1); } while (0) +#define panic(...) \ + do { fprintf (stderr, __VA_ARGS__); exit (-1); } while (0) long dummy; static long iterations = 10000; -static int maxlevel = 100; #define KB 1024 #define MB (1024*1024) @@ -96,7 +95,7 @@ f1 (int level, int maxlevel, double *step) } static void -doit (const char *label) +doit (const char *label, int maxlevel) { double step, min_step, first_step, sum_step; int i; @@ -218,10 +217,11 @@ measure_init (void) int main (int argc, char **argv) { - struct rlimit rlim; - - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; + int maxlevel = 100; + struct rlimit rlim = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY + }; setrlimit (RLIMIT_STACK, &rlim); memset (big, 0xaa, sizeof (big)); @@ -230,21 +230,21 @@ main (int argc, char **argv) { maxlevel = atol (argv[1]); if (argc > 2) - iterations = atol (argv[2]); + iterations = atol (argv[2]); } measure_init (); - doit ("default "); + doit ("default ", maxlevel); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE); - doit ("no cache "); + doit ("no cache ", maxlevel); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL); - doit ("global cache "); + doit ("global cache ", maxlevel); unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD); - doit ("per-thread cache"); + doit ("per-thread cache", maxlevel); return 0; } diff --git a/src/native/external/libunwind/tests/Gtest-bt.c b/src/native/external/libunwind/tests/Gtest-bt.c index 809970db93d57e..d1ac6f3d421cb0 100644 --- a/src/native/external/libunwind/tests/Gtest-bt.c +++ b/src/native/external/libunwind/tests/Gtest-bt.c @@ -38,17 +38,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include +#include #include #include #include -#define panic(args...) \ - { fprintf (stderr, args); exit (-1); } +#include "ident.h" + +#define panic(...) \ + { fprintf (stderr, __VA_ARGS__); exit (-1); } #define SIG_STACK_SIZE 0x100000 int verbose; int num_errors; +sigjmp_buf env; /* These variables are global because they * cause the signal stack to overflow */ @@ -102,6 +106,10 @@ do_backtrace (void) } #endif printf ("\n"); + + name[0] = '\0'; + if (unw_get_elf_filename (&cursor, name, sizeof (name), &off) == UNW_ESUCCESS) + printf ("\t[%s+0x%lx]\n", name, (long) off); } ret = unw_step (&cursor); @@ -137,8 +145,8 @@ foo (long val UNUSED) void bar (long v) { - extern long f (long); int arr[v]; + arr[0] = 0; /* This is a vain attempt to use up lots of registers to force the frame-chain info to be saved on the memory stack on ia64. @@ -166,7 +174,17 @@ bar (long v) } void -sighandler (int signal, void *siginfo UNUSED, void *context) +segv_handler (int signal, siginfo_t *siginfo UNUSED, void *context UNUSED) +{ + if (verbose) + fprintf (stderr, "segv_handler: got signal %d\n", signal); + + do_backtrace (); + siglongjmp (env, 42); +} + +void +sighandler (int signal, siginfo_t *siginfo UNUSED, void *context) { ucontext_t *uc UNUSED; int sp; @@ -175,7 +193,7 @@ sighandler (int signal, void *siginfo UNUSED, void *context) if (verbose) { - printf ("sighandler: got signal %d, sp=%p", signal, &sp); + printf ("sighandler: got signal %d, sp=%p", signal, (void *)&sp); #if UNW_TARGET_IA64 # if defined(__linux__) || defined __sun printf (" @ %lx", uc->uc_mcontext.sc_ip); @@ -202,9 +220,9 @@ sighandler (int signal, void *siginfo UNUSED, void *context) printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip); # endif #elif UNW_TARGET_AARCH64 -# if defined(__QNXNTO__) +# if defined(__QNX__) fprintf (stderr, " @ %#010lx", (unsigned long) uc->uc_mcontext.cpu.elr); -# endif /* defined(__QNXNTO__) */ +# endif /* defined(__QNX__) */ #endif printf ("\n"); } @@ -227,8 +245,8 @@ main (int argc, char **argv UNUSED) bar (1); memset (&act, 0, sizeof (act)); - act.sa_handler = (void (*)(int)) sighandler; act.sa_flags = SA_SIGINFO; + act.sa_sigaction = sighandler; if (sigaction (SIGTERM, &act, NULL) < 0) panic ("sigaction: %s\n", strerror (errno)); @@ -236,6 +254,20 @@ main (int argc, char **argv UNUSED) printf ("\nBacktrace across signal handler:\n"); kill (getpid (), SIGTERM); + if (verbose) + printf ("\nBacktrace across SIGSEGV handler:\n"); + + act.sa_sigaction = segv_handler; + if (sigaction (SIGSEGV, &act, NULL) < 0) + panic ("sigaction: %s\n", strerror (errno)); + + if (sigsetjmp (env, 1) == 0) + { + /* Make a NULL pointer dereference. */ + int *bad_ptr = NULL; + *bad_ptr = 0; + } + #ifdef HAVE_SIGALTSTACK if (verbose) printf ("\nBacktrace across signal handler on alternate stack:\n"); @@ -248,8 +280,8 @@ main (int argc, char **argv UNUSED) panic ("sigaltstack: %s\n", strerror (errno)); memset (&act, 0, sizeof (act)); - act.sa_handler = (void (*)(int)) sighandler; act.sa_flags = SA_ONSTACK | SA_SIGINFO; + act.sa_sigaction = sighandler; if (sigaction (SIGTERM, &act, NULL) < 0) panic ("sigaction: %s\n", strerror (errno)); kill (getpid (), SIGTERM); diff --git a/src/native/external/libunwind/tests/Gtest-concurrent.c b/src/native/external/libunwind/tests/Gtest-concurrent.c index 6f3447fd844da1..64c337ff63c1f7 100644 --- a/src/native/external/libunwind/tests/Gtest-concurrent.c +++ b/src/native/external/libunwind/tests/Gtest-concurrent.c @@ -39,8 +39,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define NTHREADS 128 -#define panic(args...) \ - do { fprintf (stderr, args); ++nerrors; } while (0) +#define panic(...) \ + do { fprintf (stderr, __VA_ARGS__); ++nerrors; } while (0) int verbose; int nerrors; diff --git a/src/native/external/libunwind/tests/Gtest-exc.c b/src/native/external/libunwind/tests/Gtest-exc.c index 29c1993f2dee8b..5474fa41b28900 100644 --- a/src/native/external/libunwind/tests/Gtest-exc.c +++ b/src/native/external/libunwind/tests/Gtest-exc.c @@ -40,8 +40,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # include #endif -#define panic(args...) \ - { ++nerrors; fprintf (stderr, args); } +#define panic(...) \ + { ++nerrors; fprintf (stderr, __VA_ARGS__); } int nerrors = 0; int verbose = 0; @@ -112,7 +112,7 @@ a (int n) if (verbose) printf ("a(n=%d): sp=%p bsp=0x%lx\n", - n, &stack, (unsigned long) get_bsp ()); + n, (void *)&stack, (unsigned long) get_bsp ()); if (n > 0) a (n - 1); @@ -122,7 +122,7 @@ a (int n) if (verbose) { printf ("exception handler: here we go (sp=%p, bsp=0x%lx)...\n", - &stack, (unsigned long) get_bsp ()); + (void *)&stack, (unsigned long) get_bsp ()); /* This call works around a bug in gcc (up-to pre3.4) which causes invalid assembly code to be generated when __builtin_ia64_bsp() gets predicated. */ diff --git a/src/native/external/libunwind/tests/Gtest-init.cxx b/src/native/external/libunwind/tests/Gtest-init.cxx index afded019273f53..b50c65979bbab0 100644 --- a/src/native/external/libunwind/tests/Gtest-init.cxx +++ b/src/native/external/libunwind/tests/Gtest-init.cxx @@ -49,7 +49,8 @@ static void do_backtrace (void) { char name[128], off[32]; - unw_word_t ip, offset; + char filename[256]; + unw_word_t ip, offset, file_offset; unw_cursor_t cursor; unw_context_t uc; int ret, count = 0; @@ -63,12 +64,17 @@ do_backtrace (void) name[0] = '\0'; off[0] = '\0'; if (unw_get_proc_name (&cursor, name, sizeof (name), &offset) == 0 - && offset > 0) - snprintf (off, sizeof (off), "+0x%lx", (long) offset); + && offset > 0) + snprintf (off, sizeof (off), "+0x%lx", (long) offset); if (verbose) - printf (" [%lx] <%s%s>\n", (long) ip, name, off); + printf (" [%lx] <%s%s>\n", (long) ip, name, off); + + if (unw_get_elf_filename (&cursor, filename, sizeof (filename), &file_offset) == 0) + if (verbose) + printf (" [%s+0x%lx]\n", filename, (long) file_offset); + if (++count > 32) - panic ("FAILURE: didn't reach beginning of unwind-chain\n"); + panic ("FAILURE: didn't reach beginning of unwind-chain\n"); } while ((ret = unw_step (&cursor)) > 0); diff --git a/src/native/external/libunwind/tests/Gtest-resume-sig.c b/src/native/external/libunwind/tests/Gtest-resume-sig.c index 18ec65da713440..cda8a5cd83e326 100644 --- a/src/native/external/libunwind/tests/Gtest-resume-sig.c +++ b/src/native/external/libunwind/tests/Gtest-resume-sig.c @@ -41,8 +41,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # include #endif -#define panic(args...) \ - do { fprintf (stderr, args); ++nerrors; } while (0) +#define panic(...) \ + do { fprintf (stderr, __VA_ARGS__); ++nerrors; } while (0) int verbose; int nerrors; @@ -119,11 +119,6 @@ handler (int sig) if ((ret = unw_step (&c)) < 0) /* step to kill() */ panic ("unw_step(2) failed: ret=%d\n", ret); -#if defined(UNW_TARGET_TILEGX) - if ((ret = unw_step (&c)) < 0) /* step to signal trampoline */ - panic ("unw_step(2) failed: ret=%d\n", ret); -#endif - if ((ret = unw_get_reg (&c, UNW_REG_IP, &ip)) < 0) panic ("unw_get_reg(IP) failed: ret=%d\n", ret); if (verbose) diff --git a/src/native/external/libunwind/tests/Gtest-trace.c b/src/native/external/libunwind/tests/Gtest-trace.c index 5f22f1fb779865..a505f0ccd5e4ce 100644 --- a/src/native/external/libunwind/tests/Gtest-trace.c +++ b/src/native/external/libunwind/tests/Gtest-trace.c @@ -40,8 +40,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#define panic(args...) \ - { fprintf (stderr, args); exit (-1); } +#include "ident.h" + +#define panic(...) \ + { fprintf (stderr, __VA_ARGS__); exit (-1); } #define SIG_STACK_SIZE 0x100000 @@ -80,7 +82,7 @@ do_backtrace (void) if (ret < 0) { unw_get_reg (&cursor, UNW_REG_IP, &ip); - printf ("FAILURE: unw_step() returned %d for ip=%lx\n", ret, (long) ip); + printf ("FAILURE: unw_step() returned %d for ip=%#010lx\n", ret, (long) ip); ++num_errors; } @@ -121,7 +123,7 @@ do_backtrace (void) if (n == m) for (i = 1; i < n; ++i) /* Allow one in difference in comparison, trace returns adjusted addresses. */ - if (labs((unw_word_t) addresses[1][i] - (unw_word_t) addresses[2][i]) > 1) + if (labs (addresses[1][i] - addresses[2][i]) > 1) { printf ("FAILURE: backtrace() and unw_backtrace() addresses differ at %d: %p vs. %p\n", i, addresses[1][i], addresses[2][i]); @@ -131,7 +133,7 @@ do_backtrace (void) if (n == depth+1) for (i = 1; i < depth; ++i) /* Allow one in difference in comparison, trace returns adjusted addresses. */ - if (labs((unw_word_t) addresses[0][i] - (unw_word_t) addresses[1][i]) > 1) + if (labs (addresses[0][i] - addresses[1][i]) > 1) { printf ("FAILURE: unw_step() loop and backtrace() addresses differ at %d: %p vs. %p\n", i, addresses[0][i], addresses[1][i]); @@ -163,7 +165,7 @@ do_backtrace_with_context(void *context) if (ret < 0) { unw_get_reg (&cursor, UNW_REG_IP, &ip); - printf ("FAILURE: unw_step() returned %d for ip=%lx\n", ret, (long) ip); + printf ("FAILURE: unw_step() returned %d for ip=%#010lx\n", ret, (long) ip); ++num_errors; } @@ -189,9 +191,9 @@ do_backtrace_with_context(void *context) if (m == depth + 1) for (i = 0; i < depth; ++i) /* Allow one in difference in comparison, trace returns adjusted addresses. */ - if (labs((unw_word_t) addresses[0][i] - (unw_word_t) addresses[1][i]) > 1) + if ( labs(addresses[0][i] - addresses[1][i]) > 1) { - printf ("FAILURE: unw_step() loop and uwn_backtrace2() addresses differ at %d: %p vs. %p\n", + printf ("FAILURE: unw_step() loop and unw_backtrace2() addresses differ at %d: %p vs. %p\n", i, addresses[0][i], addresses[1][i]); ++num_errors; } @@ -206,8 +208,8 @@ foo (long val UNUSED) void bar (long v) { - extern long f (long); int arr[v]; + arr[0] = 0; /* This is a vain attempt to use up lots of registers to force the frame-chain info to be saved on the memory stack on ia64. @@ -235,7 +237,7 @@ bar (long v) } void -sighandler (int signal, void *siginfo UNUSED, void *context) +sighandler (int signal, siginfo_t *siginfo UNUSED, void *context) { ucontext_t *uc UNUSED; int sp; @@ -244,10 +246,10 @@ sighandler (int signal, void *siginfo UNUSED, void *context) if (verbose) { - printf ("sighandler: got signal %d, sp=%p", signal, &sp); + printf ("sighandler: got signal %d, sp=%p", signal, (void *)&sp); #if UNW_TARGET_IA64 # if defined(__linux__) - printf (" @ %lx", uc->uc_mcontext.sc_ip); + printf (" @ %#010lx", uc->uc_mcontext.sc_ip); # else { uint16_t reason; @@ -255,26 +257,26 @@ sighandler (int signal, void *siginfo UNUSED, void *context) __uc_get_reason (uc, &reason); __uc_get_ip (uc, &ip); - printf (" @ %lx (reason=%d)", ip, reason); + printf (" @ %#010lx (reason=%d)", ip, reason); } # endif #elif UNW_TARGET_X86 #if defined __linux__ - printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]); + printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]); #elif defined __FreeBSD__ - printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_eip); + printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.mc_eip); #endif #elif UNW_TARGET_X86_64 #if defined __linux__ || defined __sun - printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]); + printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]); #elif defined __FreeBSD__ - printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip); + printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.mc_rip); #endif #elif defined UNW_TARGET_ARM #if defined __linux__ - printf (" @ %lx", (unsigned long) uc->uc_mcontext.arm_pc); + printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.arm_pc); #elif defined __FreeBSD__ - printf (" @ %lx", (unsigned long) uc->uc_mcontext.__gregs[_REG_PC]); + printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.__gregs[_REG_PC]); #endif #endif printf ("\n"); @@ -299,7 +301,7 @@ main (int argc, char **argv UNUSED) bar (1); memset (&act, 0, sizeof (act)); - act.sa_handler = (void (*)(int)) sighandler; + act.sa_sigaction = sighandler; act.sa_flags = SA_SIGINFO; if (sigaction (SIGTERM, &act, NULL) < 0) panic ("sigaction: %s\n", strerror (errno)); @@ -320,7 +322,7 @@ main (int argc, char **argv UNUSED) panic ("sigaltstack: %s\n", strerror (errno)); memset (&act, 0, sizeof (act)); - act.sa_handler = (void (*)(int)) sighandler; + act.sa_sigaction = sighandler; act.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaction (SIGTERM, &act, NULL) < 0) panic ("sigaction: %s\n", strerror (errno)); diff --git a/src/native/external/libunwind/tests/Gx64-test-dwarf-expressions.c b/src/native/external/libunwind/tests/Gx64-test-dwarf-expressions.c index 209f87131248da..486722d0047923 100644 --- a/src/native/external/libunwind/tests/Gx64-test-dwarf-expressions.c +++ b/src/native/external/libunwind/tests/Gx64-test-dwarf-expressions.c @@ -3,12 +3,13 @@ #include #include +#include "compiler.h" static int verbose; static int nerrors; -#define panic(args...) \ - do { printf (args); ++nerrors; } while (0) +#define panic(...) \ + do { printf (__VA_ARGS__); ++nerrors; } while (0) // Assembly routine which sets up the stack for the test then calls another one // which clobbers the stack, and which in turn calls recover_register below @@ -46,7 +47,7 @@ extern int64_t recover_register(int64_t regnum, int64_t height) } int -main (int argc, char **argv) +main (int argc, char **argv UNUSED) { if (argc > 1) verbose = 1; diff --git a/src/native/external/libunwind/tests/Lrs-race.c b/src/native/external/libunwind/tests/Lrs-race.c index 6fe4972020d022..ec9dcf990fae9a 100644 --- a/src/native/external/libunwind/tests/Lrs-race.c +++ b/src/native/external/libunwind/tests/Lrs-race.c @@ -43,7 +43,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ int verbose; -void +NOINLINE void foo_0 (void) { void *buf[20]; @@ -53,7 +53,7 @@ foo_0 (void) abort (); } -void +NOINLINE void foo_1 (void) { void *buf[20]; @@ -63,7 +63,7 @@ foo_1 (void) abort (); } -void +NOINLINE void foo_2 (void) { void *buf[20]; @@ -73,7 +73,7 @@ foo_2 (void) abort (); } -void +NOINLINE void foo_3 (void) { void *buf[20]; @@ -83,7 +83,7 @@ foo_3 (void) abort (); } -void +NOINLINE void foo_4 (void) { void *buf[20]; @@ -93,7 +93,7 @@ foo_4 (void) abort (); } -void +NOINLINE void foo_5 (void) { void *buf[20]; @@ -103,7 +103,7 @@ foo_5 (void) abort (); } -void +NOINLINE void foo_6 (void) { void *buf[20]; @@ -113,7 +113,7 @@ foo_6 (void) abort (); } -void +NOINLINE void foo_7 (void) { void *buf[20]; @@ -123,7 +123,7 @@ foo_7 (void) abort (); } -void +NOINLINE void foo_8 (void) { void *buf[20]; @@ -133,7 +133,7 @@ foo_8 (void) abort (); } -void +NOINLINE void foo_9 (void) { void *buf[20]; @@ -143,7 +143,7 @@ foo_9 (void) abort (); } -void +NOINLINE void foo_10 (void) { void *buf[20]; @@ -153,7 +153,7 @@ foo_10 (void) abort (); } -void +NOINLINE void foo_11 (void) { void *buf[20]; @@ -163,7 +163,7 @@ foo_11 (void) abort (); } -void +NOINLINE void foo_12 (void) { void *buf[20]; @@ -173,7 +173,7 @@ foo_12 (void) abort (); } -void +NOINLINE void foo_13 (void) { void *buf[20]; @@ -183,7 +183,7 @@ foo_13 (void) abort (); } -void +NOINLINE void foo_14 (void) { void *buf[20]; @@ -193,7 +193,7 @@ foo_14 (void) abort (); } -void +NOINLINE void foo_15 (void) { void *buf[20]; @@ -203,7 +203,7 @@ foo_15 (void) abort (); } -void +NOINLINE void foo_16 (void) { void *buf[20]; @@ -213,7 +213,7 @@ foo_16 (void) abort (); } -void +NOINLINE void foo_17 (void) { void *buf[20]; @@ -223,7 +223,7 @@ foo_17 (void) abort (); } -void +NOINLINE void foo_18 (void) { void *buf[20]; @@ -233,7 +233,7 @@ foo_18 (void) abort (); } -void +NOINLINE void foo_19 (void) { void *buf[20]; @@ -243,7 +243,7 @@ foo_19 (void) abort (); } -void +NOINLINE void foo_20 (void) { void *buf[20]; @@ -253,7 +253,7 @@ foo_20 (void) abort (); } -void +NOINLINE void foo_21 (void) { void *buf[20]; @@ -263,7 +263,7 @@ foo_21 (void) abort (); } -void +NOINLINE void foo_22 (void) { void *buf[20]; @@ -273,7 +273,7 @@ foo_22 (void) abort (); } -void +NOINLINE void foo_23 (void) { void *buf[20]; @@ -283,7 +283,7 @@ foo_23 (void) abort (); } -void +NOINLINE void foo_24 (void) { void *buf[20]; @@ -293,7 +293,7 @@ foo_24 (void) abort (); } -void +NOINLINE void foo_25 (void) { void *buf[20]; @@ -303,7 +303,7 @@ foo_25 (void) abort (); } -void +NOINLINE void foo_26 (void) { void *buf[20]; @@ -313,7 +313,7 @@ foo_26 (void) abort (); } -void +NOINLINE void foo_27 (void) { void *buf[20]; @@ -323,7 +323,7 @@ foo_27 (void) abort (); } -void +NOINLINE void foo_28 (void) { void *buf[20]; @@ -333,7 +333,7 @@ foo_28 (void) abort (); } -void +NOINLINE void foo_29 (void) { void *buf[20]; @@ -343,7 +343,7 @@ foo_29 (void) abort (); } -void +NOINLINE void foo_30 (void) { void *buf[20]; @@ -353,7 +353,7 @@ foo_30 (void) abort (); } -void +NOINLINE void foo_31 (void) { void *buf[20]; @@ -363,7 +363,7 @@ foo_31 (void) abort (); } -void +NOINLINE void foo_32 (void) { void *buf[20]; @@ -373,7 +373,7 @@ foo_32 (void) abort (); } -void +NOINLINE void foo_33 (void) { void *buf[20]; @@ -383,7 +383,7 @@ foo_33 (void) abort (); } -void +NOINLINE void foo_34 (void) { void *buf[20]; @@ -393,7 +393,7 @@ foo_34 (void) abort (); } -void +NOINLINE void foo_35 (void) { void *buf[20]; @@ -403,7 +403,7 @@ foo_35 (void) abort (); } -void +NOINLINE void foo_36 (void) { void *buf[20]; @@ -413,7 +413,7 @@ foo_36 (void) abort (); } -void +NOINLINE void foo_37 (void) { void *buf[20]; @@ -423,7 +423,7 @@ foo_37 (void) abort (); } -void +NOINLINE void foo_38 (void) { void *buf[20]; @@ -433,7 +433,7 @@ foo_38 (void) abort (); } -void +NOINLINE void foo_39 (void) { void *buf[20]; @@ -443,7 +443,7 @@ foo_39 (void) abort (); } -void +NOINLINE void foo_40 (void) { void *buf[20]; @@ -453,7 +453,7 @@ foo_40 (void) abort (); } -void +NOINLINE void foo_41 (void) { void *buf[20]; @@ -463,7 +463,7 @@ foo_41 (void) abort (); } -void +NOINLINE void foo_42 (void) { void *buf[20]; @@ -473,7 +473,7 @@ foo_42 (void) abort (); } -void +NOINLINE void foo_43 (void) { void *buf[20]; @@ -483,7 +483,7 @@ foo_43 (void) abort (); } -void +NOINLINE void foo_44 (void) { void *buf[20]; @@ -493,7 +493,7 @@ foo_44 (void) abort (); } -void +NOINLINE void foo_45 (void) { void *buf[20]; @@ -503,7 +503,7 @@ foo_45 (void) abort (); } -void +NOINLINE void foo_46 (void) { void *buf[20]; @@ -513,7 +513,7 @@ foo_46 (void) abort (); } -void +NOINLINE void foo_47 (void) { void *buf[20]; @@ -523,7 +523,7 @@ foo_47 (void) abort (); } -void +NOINLINE void foo_48 (void) { void *buf[20]; @@ -533,7 +533,7 @@ foo_48 (void) abort (); } -void +NOINLINE void foo_49 (void) { void *buf[20]; @@ -543,7 +543,7 @@ foo_49 (void) abort (); } -void +NOINLINE void foo_50 (void) { void *buf[20]; @@ -553,7 +553,7 @@ foo_50 (void) abort (); } -void +NOINLINE void foo_51 (void) { void *buf[20]; @@ -563,7 +563,7 @@ foo_51 (void) abort (); } -void +NOINLINE void foo_52 (void) { void *buf[20]; @@ -573,7 +573,7 @@ foo_52 (void) abort (); } -void +NOINLINE void foo_53 (void) { void *buf[20]; @@ -583,7 +583,7 @@ foo_53 (void) abort (); } -void +NOINLINE void foo_54 (void) { void *buf[20]; @@ -593,7 +593,7 @@ foo_54 (void) abort (); } -void +NOINLINE void foo_55 (void) { void *buf[20]; @@ -603,7 +603,7 @@ foo_55 (void) abort (); } -void +NOINLINE void foo_56 (void) { void *buf[20]; @@ -613,7 +613,7 @@ foo_56 (void) abort (); } -void +NOINLINE void foo_57 (void) { void *buf[20]; @@ -623,7 +623,7 @@ foo_57 (void) abort (); } -void +NOINLINE void foo_58 (void) { void *buf[20]; @@ -633,7 +633,7 @@ foo_58 (void) abort (); } -void +NOINLINE void foo_59 (void) { void *buf[20]; @@ -643,7 +643,7 @@ foo_59 (void) abort (); } -void +NOINLINE void foo_60 (void) { void *buf[20]; @@ -653,7 +653,7 @@ foo_60 (void) abort (); } -void +NOINLINE void foo_61 (void) { void *buf[20]; @@ -663,7 +663,7 @@ foo_61 (void) abort (); } -void +NOINLINE void foo_62 (void) { void *buf[20]; @@ -673,7 +673,7 @@ foo_62 (void) abort (); } -void +NOINLINE void foo_63 (void) { void *buf[20]; @@ -683,7 +683,7 @@ foo_63 (void) abort (); } -void +NOINLINE void foo_64 (void) { void *buf[20]; @@ -693,7 +693,7 @@ foo_64 (void) abort (); } -void +NOINLINE void foo_65 (void) { void *buf[20]; @@ -703,7 +703,7 @@ foo_65 (void) abort (); } -void +NOINLINE void foo_66 (void) { void *buf[20]; @@ -713,7 +713,7 @@ foo_66 (void) abort (); } -void +NOINLINE void foo_67 (void) { void *buf[20]; @@ -723,7 +723,7 @@ foo_67 (void) abort (); } -void +NOINLINE void foo_68 (void) { void *buf[20]; @@ -733,7 +733,7 @@ foo_68 (void) abort (); } -void +NOINLINE void foo_69 (void) { void *buf[20]; @@ -743,7 +743,7 @@ foo_69 (void) abort (); } -void +NOINLINE void foo_70 (void) { void *buf[20]; @@ -753,7 +753,7 @@ foo_70 (void) abort (); } -void +NOINLINE void foo_71 (void) { void *buf[20]; @@ -763,7 +763,7 @@ foo_71 (void) abort (); } -void +NOINLINE void foo_72 (void) { void *buf[20]; @@ -773,7 +773,7 @@ foo_72 (void) abort (); } -void +NOINLINE void foo_73 (void) { void *buf[20]; @@ -783,7 +783,7 @@ foo_73 (void) abort (); } -void +NOINLINE void foo_74 (void) { void *buf[20]; @@ -793,7 +793,7 @@ foo_74 (void) abort (); } -void +NOINLINE void foo_75 (void) { void *buf[20]; @@ -803,7 +803,7 @@ foo_75 (void) abort (); } -void +NOINLINE void foo_76 (void) { void *buf[20]; @@ -813,7 +813,7 @@ foo_76 (void) abort (); } -void +NOINLINE void foo_77 (void) { void *buf[20]; @@ -823,7 +823,7 @@ foo_77 (void) abort (); } -void +NOINLINE void foo_78 (void) { void *buf[20]; @@ -833,7 +833,7 @@ foo_78 (void) abort (); } -void +NOINLINE void foo_79 (void) { void *buf[20]; @@ -843,7 +843,7 @@ foo_79 (void) abort (); } -void +NOINLINE void foo_80 (void) { void *buf[20]; @@ -853,7 +853,7 @@ foo_80 (void) abort (); } -void +NOINLINE void foo_81 (void) { void *buf[20]; @@ -863,7 +863,7 @@ foo_81 (void) abort (); } -void +NOINLINE void foo_82 (void) { void *buf[20]; @@ -873,7 +873,7 @@ foo_82 (void) abort (); } -void +NOINLINE void foo_83 (void) { void *buf[20]; @@ -883,7 +883,7 @@ foo_83 (void) abort (); } -void +NOINLINE void foo_84 (void) { void *buf[20]; @@ -893,7 +893,7 @@ foo_84 (void) abort (); } -void +NOINLINE void foo_85 (void) { void *buf[20]; @@ -903,7 +903,7 @@ foo_85 (void) abort (); } -void +NOINLINE void foo_86 (void) { void *buf[20]; @@ -913,7 +913,7 @@ foo_86 (void) abort (); } -void +NOINLINE void foo_87 (void) { void *buf[20]; @@ -923,7 +923,7 @@ foo_87 (void) abort (); } -void +NOINLINE void foo_88 (void) { void *buf[20]; @@ -933,7 +933,7 @@ foo_88 (void) abort (); } -void +NOINLINE void foo_89 (void) { void *buf[20]; @@ -943,7 +943,7 @@ foo_89 (void) abort (); } -void +NOINLINE void foo_90 (void) { void *buf[20]; @@ -953,7 +953,7 @@ foo_90 (void) abort (); } -void +NOINLINE void foo_91 (void) { void *buf[20]; @@ -963,7 +963,7 @@ foo_91 (void) abort (); } -void +NOINLINE void foo_92 (void) { void *buf[20]; @@ -973,7 +973,7 @@ foo_92 (void) abort (); } -void +NOINLINE void foo_93 (void) { void *buf[20]; @@ -983,7 +983,7 @@ foo_93 (void) abort (); } -void +NOINLINE void foo_94 (void) { void *buf[20]; @@ -993,7 +993,7 @@ foo_94 (void) abort (); } -void +NOINLINE void foo_95 (void) { void *buf[20]; @@ -1003,7 +1003,7 @@ foo_95 (void) abort (); } -void +NOINLINE void foo_96 (void) { void *buf[20]; @@ -1013,7 +1013,7 @@ foo_96 (void) abort (); } -void +NOINLINE void foo_97 (void) { void *buf[20]; @@ -1023,7 +1023,7 @@ foo_97 (void) abort (); } -void +NOINLINE void foo_98 (void) { void *buf[20]; @@ -1033,7 +1033,7 @@ foo_98 (void) abort (); } -void +NOINLINE void foo_99 (void) { void *buf[20]; @@ -1043,7 +1043,7 @@ foo_99 (void) abort (); } -void +NOINLINE void foo_100 (void) { void *buf[20]; @@ -1053,7 +1053,7 @@ foo_100 (void) abort (); } -void +NOINLINE void foo_101 (void) { void *buf[20]; @@ -1063,7 +1063,7 @@ foo_101 (void) abort (); } -void +NOINLINE void foo_102 (void) { void *buf[20]; @@ -1073,7 +1073,7 @@ foo_102 (void) abort (); } -void +NOINLINE void foo_103 (void) { void *buf[20]; @@ -1083,7 +1083,7 @@ foo_103 (void) abort (); } -void +NOINLINE void foo_104 (void) { void *buf[20]; @@ -1093,7 +1093,7 @@ foo_104 (void) abort (); } -void +NOINLINE void foo_105 (void) { void *buf[20]; @@ -1103,7 +1103,7 @@ foo_105 (void) abort (); } -void +NOINLINE void foo_106 (void) { void *buf[20]; @@ -1113,7 +1113,7 @@ foo_106 (void) abort (); } -void +NOINLINE void foo_107 (void) { void *buf[20]; @@ -1123,7 +1123,7 @@ foo_107 (void) abort (); } -void +NOINLINE void foo_108 (void) { void *buf[20]; @@ -1133,7 +1133,7 @@ foo_108 (void) abort (); } -void +NOINLINE void foo_109 (void) { void *buf[20]; @@ -1143,7 +1143,7 @@ foo_109 (void) abort (); } -void +NOINLINE void foo_110 (void) { void *buf[20]; @@ -1153,7 +1153,7 @@ foo_110 (void) abort (); } -void +NOINLINE void foo_111 (void) { void *buf[20]; @@ -1163,7 +1163,7 @@ foo_111 (void) abort (); } -void +NOINLINE void foo_112 (void) { void *buf[20]; @@ -1173,7 +1173,7 @@ foo_112 (void) abort (); } -void +NOINLINE void foo_113 (void) { void *buf[20]; @@ -1183,7 +1183,7 @@ foo_113 (void) abort (); } -void +NOINLINE void foo_114 (void) { void *buf[20]; @@ -1193,7 +1193,7 @@ foo_114 (void) abort (); } -void +NOINLINE void foo_115 (void) { void *buf[20]; @@ -1203,7 +1203,7 @@ foo_115 (void) abort (); } -void +NOINLINE void foo_116 (void) { void *buf[20]; @@ -1213,7 +1213,7 @@ foo_116 (void) abort (); } -void +NOINLINE void foo_117 (void) { void *buf[20]; @@ -1223,7 +1223,7 @@ foo_117 (void) abort (); } -void +NOINLINE void foo_118 (void) { void *buf[20]; @@ -1233,7 +1233,7 @@ foo_118 (void) abort (); } -void +NOINLINE void foo_119 (void) { void *buf[20]; @@ -1243,7 +1243,7 @@ foo_119 (void) abort (); } -void +NOINLINE void foo_120 (void) { void *buf[20]; @@ -1253,7 +1253,7 @@ foo_120 (void) abort (); } -void +NOINLINE void foo_121 (void) { void *buf[20]; @@ -1263,7 +1263,7 @@ foo_121 (void) abort (); } -void +NOINLINE void foo_122 (void) { void *buf[20]; @@ -1273,7 +1273,7 @@ foo_122 (void) abort (); } -void +NOINLINE void foo_123 (void) { void *buf[20]; @@ -1283,7 +1283,7 @@ foo_123 (void) abort (); } -void +NOINLINE void foo_124 (void) { void *buf[20]; @@ -1293,7 +1293,7 @@ foo_124 (void) abort (); } -void +NOINLINE void foo_125 (void) { void *buf[20]; @@ -1303,7 +1303,7 @@ foo_125 (void) abort (); } -void +NOINLINE void foo_126 (void) { void *buf[20]; @@ -1313,7 +1313,7 @@ foo_126 (void) abort (); } -void +NOINLINE void foo_127 (void) { void *buf[20]; @@ -1323,7 +1323,7 @@ foo_127 (void) abort (); } -void +NOINLINE void foo_128 (void) { void *buf[20]; @@ -1333,7 +1333,7 @@ foo_128 (void) abort (); } -void * +NOINLINE void * bar(void *p UNUSED) { int i; diff --git a/src/native/external/libunwind/tests/Ltest-init-local-signal.c b/src/native/external/libunwind/tests/Ltest-init-local-signal.c index 4bde218f3bb2cf..052a421fd18101 100644 --- a/src/native/external/libunwind/tests/Ltest-init-local-signal.c +++ b/src/native/external/libunwind/tests/Ltest-init-local-signal.c @@ -1,4 +1,5 @@ #include "libunwind.h" +#include "compiler.h" #include #include #include @@ -9,6 +10,14 @@ #include #include +static const int max_steps = 10; + +#if defined __FreeBSD__ +#define TRAMPOLINE_DEPTH 4 +#else +#define TRAMPOLINE_DEPTH 2 +#endif + int stepper(unw_cursor_t* c) { int steps = 0; int ret = 1; @@ -19,6 +28,7 @@ int stepper(unw_cursor_t* c) { break; } steps++; + if (steps > max_steps) break; } return steps; } @@ -26,7 +36,7 @@ int stepper(unw_cursor_t* c) { /* Verify that we can step from both ucontext, and from getcontext() * roughly the same. This tests that the IP from ucontext is used * correctly (see impl of unw_init_local2) */ -void handler(int num, siginfo_t* info, void* ucontext) { +void handler(int num UNUSED, siginfo_t* info UNUSED, void* ucontext) { unw_cursor_t c; unw_context_t context; unw_getcontext(&context); @@ -38,11 +48,11 @@ void handler(int num, siginfo_t* info, void* ucontext) { (void)ret; assert(!ret); int getcontext_steps = stepper(&c); - if (ucontext_steps == getcontext_steps - 2) { + if (ucontext_steps == getcontext_steps - TRAMPOLINE_DEPTH) { exit(0); } printf("unw_getcontext steps was %i, ucontext steps was %i, should be %i\n", - getcontext_steps, ucontext_steps, getcontext_steps - 2); + getcontext_steps, ucontext_steps, getcontext_steps - TRAMPOLINE_DEPTH); exit(-1); } diff --git a/src/native/external/libunwind/tests/Ltest-mem-validate.c b/src/native/external/libunwind/tests/Ltest-mem-validate.c index 251c34ea76db4a..6db7958109f725 100644 --- a/src/native/external/libunwind/tests/Ltest-mem-validate.c +++ b/src/native/external/libunwind/tests/Ltest-mem-validate.c @@ -38,12 +38,14 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#define panic(args...) \ - { fprintf (stderr, args); exit (-1); } +#define panic(...) \ + { fprintf (stderr, __VA_ARGS__); exit (-1); } void * stack_start; +long page_size; -#define PAGE_SIZE 4096 +#define STEPS 5 +#define STACK_SLICE (sizeof(unw_cursor_t) + sizeof(unw_context_t)) void do_backtrace (void) { @@ -52,8 +54,7 @@ void do_backtrace (void) (steps > 5) before touching the forbidden region in the stack, at which point the unwinding should stop gracefully. */ - mprotect((void*)((uintptr_t)stack_start & ~(PAGE_SIZE - 1)), - PAGE_SIZE, PROT_NONE); + mprotect(stack_start, page_size, PROT_NONE); unw_cursor_t cursor; unw_word_t ip, sp; @@ -71,7 +72,7 @@ void do_backtrace (void) unw_get_reg (&cursor, UNW_REG_SP, &sp); ret = unw_step (&cursor); - printf("ip=%lx, sp=%lx -> %d\n", ip, sp, ret); + printf("ip=%p, sp=%p -> %d\n", (void *)ip, (void *)sp, ret); if (ret < 0) { unw_get_reg (&cursor, UNW_REG_IP, &ip); @@ -80,24 +81,23 @@ void do_backtrace (void) } while (ret > 0); - if (steps < 5) + if (steps < STEPS) { - printf("not enough steps: %d, need 5\n", steps); + printf("not enough steps: %d, need %d\n", steps, STEPS); exit(-1); } printf("success, steps: %d\n", steps); - mprotect((void*)((uintptr_t)stack_start & ~(PAGE_SIZE - 1)), - PAGE_SIZE, PROT_READ|PROT_WRITE); + mprotect(stack_start, page_size, PROT_READ|PROT_WRITE); } void NOINLINE consume_and_run (int depth) { unw_cursor_t cursor; unw_context_t uc; - char string[1024]; + char string[64]; - sprintf (string, "hello %p %p\n", &cursor, &uc); + sprintf (string, "hello %p %p\n", (void *)&cursor, (void *)&uc); if (depth == 0) { do_backtrace(); } else { @@ -105,22 +105,39 @@ void NOINLINE consume_and_run (int depth) } } -int -main (int argc, char **argv UNUSED) +static int NOINLINE is_stack_downward (int *val) { int start; + + return val > &start; +} + +int +main (int argc UNUSED, char **argv UNUSED) +{ + int start, count; unw_context_t uc; unw_cursor_t cursor; - stack_start = &start; - /* - We need to make the frame at least the size protected by - the mprotect call so we are not forbidding access to - unrelated regions. - */ - char string[PAGE_SIZE]; - sprintf (string, "hello\n"); + * We need to make the frame at least the size protected by + * the mprotect call so we are not forbidding access to + * unrelated regions. + * mprotect consume_and_run stack area. + * Check whether stack grows downward or upward. + */ + page_size = sysconf(_SC_PAGESIZE); + if (is_stack_downward(&start)) + { + stack_start = (void *)(((uintptr_t)&start & ~(page_size - 1)) - page_size); + count = (uintptr_t)&start - (uintptr_t)stack_start; + } + else + { + stack_start = (void *)(((uintptr_t)&start & ~(page_size - 1)) + page_size); + count = (uintptr_t)stack_start - (uintptr_t)&start; + } + count = count / STACK_SLICE + STEPS; // Initialize pipe mem validate check, opens file descriptors unw_getcontext(&uc); @@ -149,7 +166,7 @@ main (int argc, char **argv UNUSED) } else { - consume_and_run (10); + consume_and_run (count); return 0; } diff --git a/src/native/external/libunwind/tests/Ltest-nocalloc.c b/src/native/external/libunwind/tests/Ltest-nocalloc.c index f5c31b2a3ee259..8cd365c490f9b0 100644 --- a/src/native/external/libunwind/tests/Ltest-nocalloc.c +++ b/src/native/external/libunwind/tests/Ltest-nocalloc.c @@ -56,10 +56,8 @@ calloc(size_t n, size_t s) if (in_unwind) { num_callocs++; - return NULL; - } else { - return func(n, s); } + return func(n, s); } void * @@ -72,10 +70,8 @@ malloc(size_t s) if (in_unwind) { num_mallocs++; - return NULL; - } else { - return func(s); } + return func(s); } static void diff --git a/src/native/external/libunwind/tests/Makefile.am b/src/native/external/libunwind/tests/Makefile.am index c074107008d7d7..ad28ec28701a0d 100644 --- a/src/native/external/libunwind/tests/Makefile.am +++ b/src/native/external/libunwind/tests/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/include/tdep-$(arch) -I$(top_srcdir)/src AM_CFLAGS = -fno-optimize-sibling-calls LOG_DRIVER = $(SHELL) $(UNW_TESTDRIVER) @@ -21,6 +21,8 @@ check_SCRIPTS_arch = check_SCRIPTS_cdep = check_SCRIPTS_common = run-check-namespace +XFAIL_TESTS = + if REMOTE_ONLY perf: @@ -40,6 +42,7 @@ if ARCH_PPC64 if USE_ALTIVEC noinst_PROGRAMS_arch += ppc64-test-altivec endif #USE_ALTIVEC + check_PROGRAMS_arch += ppc64-test-plt else #!ARCH_PPC64 if ARCH_X86_64 check_PROGRAMS_arch += Gx64-test-dwarf-expressions Lx64-test-dwarf-expressions x64-unwind-badjmp-signal-frame @@ -48,14 +51,13 @@ endif #!ARCH_PPC64 endif #!ARCH_IA64 if ARCH_AARCH64 - check_PROGRAMS_arch += Garm64-test-sve-signal Larm64-test-sve-signal + check_PROGRAMS_arch += Garm64-test-sve-signal Larm64-test-sve-signal \ + aarch64-test-plt aarch64-test-frame-record endif - check_PROGRAMS_cdep += Gtest-bt Ltest-bt Gtest-exc Ltest-exc \ + check_PROGRAMS_cdep += Gtest-bt Ltest-bt \ Gtest-init Ltest-init \ Gtest-concurrent Ltest-concurrent \ - Gtest-resume-sig Ltest-resume-sig \ - Gtest-resume-sig-rt Ltest-resume-sig-rt \ Gtest-trace Ltest-trace \ Ltest-mem-validate \ test-async-sig test-flush-cache test-init-remote \ @@ -69,10 +71,20 @@ if !ARCH_IA64 check_PROGRAMS_cdep += Ltest-init-local-signal endif +# Tests that exercise unw_resume, which is only unsupported on some targets +if ENABLE_UNW_RESUME_TESTS +check_PROGRAMS_cdep += Gtest-exc Ltest-exc \ + Gtest-resume-sig Ltest-resume-sig \ + Gtest-resume-sig-rt Ltest-resume-sig-rt +endif + if BUILD_PTRACE check_SCRIPTS_cdep += run-ptrace-mapper run-ptrace-misc check_PROGRAMS_cdep += test-ptrace noinst_PROGRAMS_cdep += mapper test-ptrace-misc +if ARCH_X86 + XFAIL_TESTS += test-ptrace +endif endif if BUILD_SETJMP @@ -82,6 +94,10 @@ endif if SUPPORT_CXX_EXCEPTIONS check_PROGRAMS_cdep += Ltest-cxx-exceptions endif +# Temporarily XFAIL this test for riscv64 until #519 is fixed +if ARCH_RISCV + XFAIL_TESTS += Ltest-cxx-exceptions +endif if OS_LINUX if BUILD_COREDUMP @@ -111,9 +127,10 @@ check_PROGRAMS = $(check_PROGRAMS_common) $(check_PROGRAMS_cdep) \ check_SCRIPTS = $(check_SCRIPTS_common) $(check_SCRIPTS_cdep) \ $(check_SCRIPTS_arch) - -TESTS = $(check_PROGRAMS) $(check_SCRIPTS) -XFAIL_TESTS = +TESTS = $(check_PROGRAMS) +if !CROSS_BUILD + TESTS += $(check_SCRIPTS) +endif if ARCH_IA64 check_PROGRAMS_cdep += Gtest-dyn1 Ltest-dyn1 @@ -143,12 +160,13 @@ endif # This is meant for multilib binaries, -m32. # ptrace gives EBADREG when testing, # but generally everything else works. -if NO_PTRACE_TEST - XFAIL_TESTS += run-ptrace-mapper test-ptrace Ltest-init-local-signal +if XFAIL_PTRACE_TEST + XFAIL_TESTS += run-ptrace-mapper endif noinst_PROGRAMS = $(noinst_PROGRAMS_common) $(noinst_PROGRAMS_cdep) \ $(noinst_PROGRAMS_arch) +noinst_HEADERS = ident.h Lia64_test_readonly_SOURCES = Lia64-test-readonly.c ia64-test-readonly-asm.S Gia64_test_readonly_SOURCES = Gia64-test-readonly.c ia64-test-readonly-asm.S @@ -163,6 +181,7 @@ Gia64_test_nat_SOURCES = Gia64-test-nat.c ia64-test-nat-asm.S ia64_test_dyn1_SOURCES = ia64-test-dyn1.c ia64-dyn-asm.S flush-cache.S \ flush-cache.h ppc64_test_altivec_SOURCES = ppc64-test-altivec.c ppc64-test-altivec-utils.c +ppc64_test_plt_SOURCES = ppc64-test-plt.c Gx64_test_dwarf_expressions_SOURCES = Gx64-test-dwarf-expressions.c \ @@ -172,8 +191,14 @@ Lx64_test_dwarf_expressions_SOURCES = Lx64-test-dwarf-expressions.c \ Garm64_test_sve_signal_SOURCES = Garm64-test-sve-signal.c Larm64_test_sve_signal_SOURCES = Larm64-test-sve-signal.c -Garm64_test_sve_signal_CFLAGS = -fno-inline -march=native -Larm64_test_sve_signal_CFLAGS = -fno-inline -march=native +aarch64_test_plt_SOURCES = aarch64-test-plt.c +aarch64_test_frame_record_SOURCES = aarch64-test-frame-record.c + +if COMPILER_SUPPORTS_MARCH_ARMV8_A_SVE + Garm64_test_sve_signal_CFLAGS = -fno-inline -march=armv8-a+sve + Larm64_test_sve_signal_CFLAGS = -fno-inline -march=armv8-a+sve +endif +XFAIL_TESTS += Garm64-test-sve-signal Larm64-test-sve-signal Gtest_init_SOURCES = Gtest-init.cxx Ltest_init_SOURCES = Ltest-init.cxx @@ -213,7 +238,7 @@ endif LIBUNWIND_setjmp = $(top_builddir)/src/libunwind-setjmp.la \ $(LIBUNWIND_ELF) $(LIBUNWIND) -test_async_sig_LDADD = $(LIBUNWIND_local) -lpthread +test_async_sig_LDADD = $(LIBUNWIND_local) $(PTHREADS_LIB) test_flush_cache_LDADD = $(LIBUNWIND_local) test_init_remote_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) test_mem_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) @@ -222,16 +247,16 @@ test_ptrace_LDADD = $(LIBUNWIND_ptrace) $(LIBUNWIND) test_proc_info_LDADD = $(LIBUNWIND) test_static_link_LDADD = $(LIBUNWIND) test_strerror_LDADD = $(LIBUNWIND) -Lrs_race_LDADD = $(LIBUNWIND_local) -lpthread +Lrs_race_LDADD = $(LIBUNWIND_local) $(PTHREADS_LIB) Ltest_varargs_LDADD = $(LIBUNWIND_local) Ltest_init_local_signal_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Gtest_bt_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) -Gtest_concurrent_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) -lpthread +Gtest_concurrent_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) $(PTHREADS_LIB) x64_unwind_badjmp_signal_frame_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Gtest_dyn1_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Gtest_exc_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) -Gtest_init_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) @BACKTRACELIB@ +Gtest_init_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) $(BACKTRACELIB) Gtest_resume_sig_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Gtest_resume_sig_rt_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Gperf_simple_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) @@ -239,13 +264,13 @@ Gtest_trace_LDADD=$(LIBUNWIND) $(LIBUNWIND_local) Gperf_trace_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Ltest_bt_LDADD = $(LIBUNWIND_local) -Ltest_concurrent_LDADD = $(LIBUNWIND_local) -lpthread +Ltest_concurrent_LDADD = $(LIBUNWIND_local) $(PTHREADS_LIB) Ltest_cxx_exceptions_LDADD = $(LIBUNWIND_local) Ltest_dyn1_LDADD = $(LIBUNWIND_local) Ltest_exc_LDADD = $(LIBUNWIND_local) Ltest_init_LDADD = $(LIBUNWIND_local) -Ltest_nomalloc_LDADD = $(LIBUNWIND_local) @DLLIB@ -Ltest_nocalloc_LDADD = $(LIBUNWIND_local) @DLLIB@ -lpthread +Ltest_nomalloc_LDADD = $(LIBUNWIND_local) $(DLLIB) +Ltest_nocalloc_LDADD = $(LIBUNWIND_local) $(DLLIB) $(PTHREADS_LIB) Ltest_resume_sig_LDADD = $(LIBUNWIND_local) Ltest_resume_sig_rt_LDADD = $(LIBUNWIND_local) Lperf_simple_LDADD = $(LIBUNWIND_local) @@ -257,7 +282,7 @@ test_setjmp_LDADD = $(LIBUNWIND_setjmp) ia64_test_setjmp_LDADD = $(LIBUNWIND_setjmp) if BUILD_COREDUMP -test_coredump_unwind_LDADD = $(LIBUNWIND_coredump) $(LIBUNWIND) @BACKTRACELIB@ +test_coredump_unwind_LDADD = $(LIBUNWIND_coredump) $(LIBUNWIND) $(BACKTRACELIB) endif Gia64_test_nat_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) @@ -271,9 +296,12 @@ Lia64_test_readonly_LDADD = $(LIBUNWIND_local) ia64_test_dyn1_LDADD = $(LIBUNWIND) ia64_test_sig_LDADD = $(LIBUNWIND) ppc64_test_altivec_LDADD = $(LIBUNWIND) +ppc64_test_plt_LDADD = $(LIBUNWIND) Gx64_test_dwarf_expressions_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Lx64_test_dwarf_expressions_LDADD = $(LIBUNWIND_local) Garm64_test_sve_signal_LDADD = $(LIBUNWIND) $(LIBUNWIND_local) Larm64_test_sve_signal_LDADD = $(LIBUNWIND_local) +aarch64_test_plt_LDADD = $(LIBUNWIND) +aarch64_test_frame_record_LDADD = $(LIBUNWIND) diff --git a/src/native/external/libunwind/tests/aarch64-test-frame-record.c b/src/native/external/libunwind/tests/aarch64-test-frame-record.c new file mode 100644 index 00000000000000..c2c2c2dde79ab5 --- /dev/null +++ b/src/native/external/libunwind/tests/aarch64-test-frame-record.c @@ -0,0 +1,390 @@ +/* + * Unittest AArch64 get_frame_state function by inspecting output at + * different points in example address spaces (from python2.7) + */ + +#include "dwarf.h" +#include "libunwind_i.h" + +int unw_is_signal_frame (unw_cursor_t *cursor) { return 0; } +int dwarf_step (struct dwarf_cursor *c) { return 0; } +#include "aarch64/Gstep.c" + +static int procedure_size; + +/* Mock access_mem implementation */ +static int +access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, + void *arg) +{ + if (write != 0) + return -1; + + const size_t mem_size = procedure_size * sizeof(uint32_t); + const void *mem_start = arg; + const void *mem_end = (const char*) arg + mem_size; + const unw_word_t *paddr = (const unw_word_t*) addr; + + if ((void*) paddr < mem_start || (void*) paddr > mem_end) + { + return -1; + } + + *val = *paddr; + return 0; +} + +//! Stub implementation of get_proc_name - returns offset to start of procedure +static int +get_proc_name (unw_addr_space_t as, unw_word_t ip, char *buf, size_t len, unw_word_t *offp, + void *arg) +{ + *offp = ip - (unw_word_t) arg; + return 0; +} + +int +main () +{ + struct unw_addr_space mock_address_space; + mock_address_space.acc.access_mem = &access_mem; + mock_address_space.acc.get_proc_name = &get_proc_name; + + frame_state_t fs; + unw_cursor_t cursor; + struct cursor *c = (struct cursor *) &cursor; + c->dwarf.as = &mock_address_space; + + /* STP_MOV_start procedure */ + { + int IpStp = 0; + int IpMov = 1; + int IpLdp1 = 4; + int IpLdp2 = 7; + + // 0000000000418254 : + unsigned int instructions[9] = { + 0xa9be7bfd, // stp x29, x30, [sp,#-32]! <= FP+LR stored + 0x910003fd, // mov x29, sp <= FP updated + 0xa90153f3, // stp x19, x20, [sp,#16] + // some instructions skipped + 0xa94153f3, // ldp x19, x20, [sp,#16] + 0xa8c27bfd, // ldp x29, x30, [sp],#32 <= FP+LR retrieved + 0x17ffff33, // b 417f50 + // some instructions skipped + 0xa94153f3, // ldp x19, x20, [sp,#16] + 0xa8c27bfd, // ldp x29, x30, [sp],#32 <= FP+LR retrieved + 0x1400de12, // b 44fb08 + }; + procedure_size = 9; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to instruction that stores FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpStp); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that updates the FP */ + c->dwarf.ip = (unw_word_t) (instructions+IpMov); + fs = get_frame_state(&cursor); + if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP was updated */ + c->dwarf.ip = (unw_word_t) (instructions+IpMov+1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to first instruction that retrieves FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction after first retrieval of FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp1+1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to second instruction that retrieves FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp2); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction after second retrieval of FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp2+1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + /* STP_start_MOV_later procedure */ + { + int IpStp = 0; + int IpMov = 2; + int IpLdp = 5; + + // 00000000004181b4 : + unsigned int instructions[7] = { + 0xa9bd7bfd, // stp x29, x30, [sp,#-48]! <= FP+LR stored + 0xf0001740, // adrp x0, 703000 + 0x910003fd, // mov x29, sp <= FP updated + 0xf943cc00, // ldr x0, [x0,#1944] + // some instructions skipped + 0xf9400bf3, // ldr x19, [sp,#16] + 0xa8c37bfd, // ldp x29, x30, [sp],#48 <= FP+LR retrieved + 0xd65f03c0, // ret + }; + procedure_size = 7; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to instruction that stores FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpStp); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP and LR are stored */ + c->dwarf.ip = (unw_word_t) (instructions+IpStp+1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1; + + /* IP is pointing to instruction that updates the FP */ + c->dwarf.ip = (unw_word_t) (instructions+IpMov); + fs = get_frame_state(&cursor); + if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP was updated */ + c->dwarf.ip = (unw_word_t) (instructions+IpMov+1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction that retrieves FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction after retrieval of FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + /* STP_MOV_later procedure */ + { + int IpStp = 3; + int IpMov = 4; + int IpLdp = 7; + + // 0000000000418370 : + uint32_t instructions[9] = { + 0xb941fc01, // ldr w1, [x0,#508] + // some instructions skipped + 0xd65f03c0, // ret + // some instructions skipped + 0x34ffffa4, // cbz w4, 41838c + 0xa9be7bfd, // stp x29, x30, [sp,#-32] <= FP+LR stored + 0x910003fd, // mov x29, sp <= FP updated + 0xf9000bf3, // str x19, [sp,#16] + 0xf9400bf3, // ldr x19, [sp,#16] + 0xa8c27bfd, // ldp x29, x30, [sp],#32 <= FP+LR retrieved + 0xd65f03c0, // ret + }; + procedure_size = 9; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to start of procedure */ + c->dwarf.ip = (unw_word_t) (instructions); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that stores FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpStp); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that updates the FP */ + c->dwarf.ip = (unw_word_t) (instructions+IpMov); + fs = get_frame_state(&cursor); + if (fs.loc != AT_SP_OFFSET || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP was updated */ + c->dwarf.ip = (unw_word_t) (instructions+IpMov+1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction that retrieves FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP and LR are retrieved */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + /* STP_POS_OFFSET procedure */ + { + int IpStp = 3; + int IpAdd = 4; + int IpLdp = 7; + + // 000000000046d1d8 : + unsigned int instructions[9] = { + 0xd10083ff, // sub sp, sp, #0x20 + 0xd2800007, // mov x7, #0x0 // #0 + // some instructions skipped + 0xd2800003, // mov x3, #0x0 // #0 + 0xa9017bfd, // stp x29, x30, [sp,#16] + 0x910043fd, // add x29, sp, #0x10 + 0xb90003ff, // str wzr, [sp] + // some instructions skipped + 0x910003bf, // mov sp, x29 + 0xa8c17bfd, // ldp x29, x30, [sp],#16 + 0xd65f03c0, // ret + }; + procedure_size = 9; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to start of procedure */ + c->dwarf.ip = (unw_word_t) (instructions); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that stores FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpStp); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that updates the FP */ + c->dwarf.ip = (unw_word_t) (instructions+IpAdd); + fs = get_frame_state(&cursor); + if (fs.loc != AT_SP_OFFSET || fs.offset != 16) return -1; + + /* IP is pointing to instruction after FP was updated */ + c->dwarf.ip = (unw_word_t) (instructions+IpAdd+1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction that retrieves FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP and LR are retrieved */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + /* STP_NEG_OFFSET procedure */ + { + int IpStp = 3; + int IpAdd = 4; + int IpLdp = 7; + + // Artificial example based on PyEval_EvalCode with negative offset STP + unsigned int instructions[9] = { + 0xd10083ff, // sub sp, sp, #0x20 + 0xd2800007, // mov x7, #0x0 // #0 + // some instructions skipped + 0xd2800003, // mov x3, #0x0 // #0 + 0xa9217bfd, // stp x29, x30, [sp,#-16] + 0x910043fd, // add x29, sp, #0x10 + 0xb90003ff, // str wzr, [sp] + // some instructions skipped + 0x910003bf, // mov sp, x29 + 0xa8c17bfd, // ldp x29, x30, [sp],#16 + 0xd65f03c0, // ret + }; + procedure_size = 9; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to start of procedure */ + c->dwarf.ip = (unw_word_t) (instructions); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that stores FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpStp); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to instruction that updates the FP */ + c->dwarf.ip = (unw_word_t) (instructions+IpAdd); + fs = get_frame_state(&cursor); + if (fs.loc != AT_SP_OFFSET || fs.offset != -16) return -1; + + /* IP is pointing to instruction after FP was updated */ + c->dwarf.ip = (unw_word_t) (instructions+IpAdd+1); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction that retrieves FP and LR */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp); + fs = get_frame_state(&cursor); + if (fs.loc != AT_FP || fs.offset != 0) return -1; + + /* IP is pointing to instruction after FP and LR are retrieved */ + c->dwarf.ip = (unw_word_t) (instructions+IpLdp+1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + /* PLT_entry procedure */ + { + // 0000000000417760 : + unsigned int instructions[4] = { + 0x900013d0, // adrp x16, 68f000 + 0xf942a211, // ldr x17, [x16,#1344] + 0x91150210, // add x16, x16, #0x540 + 0xd61f0220, // br x17 + }; + procedure_size = 4; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to start of procedure */ + c->dwarf.ip = (unw_word_t) (instructions); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to end of procedure */ + c->dwarf.ip = (unw_word_t) (instructions+procedure_size-1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + /* no_STP_MOV procedure */ + { + // 000000000041ddf0 : + uint32_t instructions[9] = { + 0xb0001641, // adrp x1, 6e6000 + 0x90000d00, // adrp x0, 5bd000 <_PyImport_StandardFiletab+0x1468> + 0x913d4023, // add x3, x1, #0xf50 + 0x911b4000, // add x0, x0, #0x6d0 + 0x91330062, // add x2, x3, #0xcc0 + 0x91374061, // add x1, x3, #0xdd0 + 0x52807ea4, // mov w4, #0x3f5 + 0xd2800003, // mov x3, #0x0 + 0x1400bccc, // b 44d140 + }; + procedure_size = 9; + + c->dwarf.as_arg = &instructions; + + /* IP is pointing to start of procedure */ + c->dwarf.ip = (unw_word_t) (instructions); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + + /* IP is pointing to end of procedure */ + c->dwarf.ip = (unw_word_t) (instructions+procedure_size-1); + fs = get_frame_state(&cursor); + if (fs.loc != NONE || fs.offset != 0) return -1; + } + + return 0; +} diff --git a/src/native/external/libunwind/tests/aarch64-test-plt.c b/src/native/external/libunwind/tests/aarch64-test-plt.c new file mode 100644 index 00000000000000..943b155bf22fc5 --- /dev/null +++ b/src/native/external/libunwind/tests/aarch64-test-plt.c @@ -0,0 +1,175 @@ +/* + * Unittest AArch64 is_plt_entry function by inspecting output at + * different points in a mock PLT address space. + */ + +#include "dwarf.h" +#include "libunwind_i.h" + +int unw_is_signal_frame (unw_cursor_t *cursor) { return 0; } +int dwarf_step (struct dwarf_cursor *c) { return 0; } +#include "aarch64/Gstep.c" + +enum +{ + ip_guard0, + ip_adrp, + ip_ldr, + ip_add, + ip_br, + ip_guard1, + + ip_program_end +}; + +/* Mock access_mem implementation */ +static int +access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, + void *arg) +{ + if (write != 0) + return -1; + + const size_t mem_size = ip_program_end * sizeof(uint32_t); + const void *mem_start = arg; + const void *mem_end = (const char*) arg + mem_size; + const unw_word_t *paddr = (const unw_word_t*) addr; + + if ((void*) paddr < mem_start || (void*) paddr > mem_end) + { + return -1; + } + + *val = *paddr; + return 0; +} + +int +main () +{ + if (target_is_big_endian()) + return 77; + + const uint32_t plt_instructions[ip_program_end] = { + 0xDEADBEEF, + 0xf0000990, // adrp x16, 540000 + 0xf9400a11, // ldr x17, [x16,#16] + 0x91004210, // add x16, x16, #0x10 + 0xd61f0220, // br x17 + 0xDEADBEEF, + }; + + uint32_t test_instructions[ip_program_end]; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + struct unw_addr_space mock_address_space; + mock_address_space.big_endian = 0; + mock_address_space.acc.access_mem = &access_mem; + + struct dwarf_cursor c; + c.as = &mock_address_space; + c.as_arg = &test_instructions; + + /* ip at adrp */ + c.ip = (unw_word_t) (test_instructions + ip_adrp); + if (is_plt_entry(&c) == 0) return -1; + + /* adrp uses different offset */ + test_instructions[ip_adrp] = 0x90272990; + if (is_plt_entry(&c) == 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ldr uses different offset */ + test_instructions[ip_ldr] = 0xf948be11; + if (is_plt_entry(&c) == 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* add uses different offset */ + test_instructions[ip_add] = 0x91726210; + if (is_plt_entry(&c) == 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_ldr is not a ldr instruction */ + test_instructions[ip_ldr] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_add is not an add instruction */ + test_instructions[ip_add] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_br is not a br instruction */ + test_instructions[ip_br] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at ldr */ + c.ip = (unw_word_t) (test_instructions + ip_ldr); + if (is_plt_entry(&c) == 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_adrp is not an adrp instruction */ + test_instructions[ip_adrp] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_add is not an add instruction */ + test_instructions[ip_add] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_br is not a br instruction */ + test_instructions[ip_br] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at add */ + c.ip = (unw_word_t) (test_instructions + ip_add); + if (is_plt_entry(&c) == 0) return -1; + + /* ip_adrp is not an adrp instruction */ + test_instructions[ip_adrp] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_ldr is not a ldr instruction */ + test_instructions[ip_ldr] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_br is not a br instruction */ + test_instructions[ip_br] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at br */ + c.ip = (unw_word_t) (test_instructions + ip_br); + if (is_plt_entry(&c) == 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_adrp is not an adrp instruction */ + test_instructions[ip_adrp] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_ldr is not a ldr instruction */ + test_instructions[ip_ldr] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_add is not an add instruction */ + test_instructions[ip_add] = 0xf154f00d; + if (is_plt_entry(&c) != 0) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at non-PLT instruction */ + c.ip = (unw_word_t) (test_instructions + ip_guard0); + if (is_plt_entry(&c)) return -1; + + /* ip at another non-PLT instruction */ + c.ip = (unw_word_t) (test_instructions + ip_guard1); + if (is_plt_entry(&c)) return -1; + + return 0; +} diff --git a/src/native/external/libunwind/tests/check-namespace.sh.in b/src/native/external/libunwind/tests/check-namespace.sh.in index a1b28a7ba37b04..c2c1bf1009e790 100644 --- a/src/native/external/libunwind/tests/check-namespace.sh.in +++ b/src/native/external/libunwind/tests/check-namespace.sh.in @@ -72,31 +72,36 @@ filter_misc () { # Ignore symbols generated by the ARM Linux default linker script. # For details see the binutils sources (src/ld/emulparams/armelf_linux.sh). - if [ ${plat} = "arm" ]; then - ignore __bss_start__ - ignore __bss_end__ - ignore __end__ - ignore _bss_end__ - fi - - if [ ${plat} = "mips" ]; then - ignore _fbss - ignore _fdata - ignore _ftext - ignore _gp - fi - - if [ ${plat} = "loongarch64" ]; then - ignore _fbss - ignore _fdata - ignore _ftext - ignore _gp - fi + case "$plat}" in + arm*|aarch64*) + ignore __bss_start__ + ignore __bss_end__ + ignore __end__ + ignore _bss_end__ + ;; + mips*) + ignore _fbss + ignore _fdata + ignore _ftext + ignore _gp + ;; + loongarch64) + ignore _fbss + ignore _fdata + ignore _ftext + ignore _gp + ;; + esac - if [ ${os} = "solaris2.11" ]; then - ignore _PROCEDURE_LINKAGE_TABLE_ - ignore _etext - fi + case "${os}" in + solaris2.11) + ignore _PROCEDURE_LINKAGE_TABLE_ + ignore _etext + ;; + nto*) + ignore _btext + ;; + esac } check_local_unw_abi () { @@ -110,6 +115,8 @@ check_local_unw_abi () { match _UL${plat}_get_proc_info_in_range match _UL${plat}_get_proc_name match _UL${plat}_get_proc_name_by_ip + match _UL${plat}_get_elf_filename + match _UL${plat}_get_elf_filename_by_ip match _UL${plat}_get_reg match _UL${plat}_get_save_loc match _UL${plat}_init_local @@ -118,6 +125,7 @@ check_local_unw_abi () { match _UL${plat}_is_signal_frame match _UL${plat}_local_addr_space match _UL${plat}_resume + match _UL${plat}_set_iterate_phdr_function match _UL${plat}_set_caching_policy match _UL${plat}_set_cache_size match _UL${plat}_set_reg @@ -177,14 +185,6 @@ check_local_unw_abi () { match _UL${plat}_dwarf_search_unwind_table match _UL${plat}_dwarf_find_unwind_table ;; - tilegx) - match _U${plat}_getcontext - match _U${plat}_is_fpreg - match _UL${plat}_dwarf_search_unwind_table - match _UL${plat}_dwarf_find_unwind_table - match _UL${plat}_local_addr_space_init - match ${plat}_lock - ;; s390x) match _U${plat}_getcontext match _U${plat}_is_fpreg @@ -231,6 +231,8 @@ check_generic_unw_abi () { match _U${plat}_get_proc_info_in_range match _U${plat}_get_proc_name match _U${plat}_get_proc_name_by_ip + match _U${plat}_get_elf_filename + match _U${plat}_get_elf_filename_by_ip match _U${plat}_get_reg match _U${plat}_get_save_loc match _U${plat}_init_local @@ -240,6 +242,7 @@ check_generic_unw_abi () { match _U${plat}_local_addr_space match _U${plat}_regname match _U${plat}_resume + match _U${plat}_set_iterate_phdr_function match _U${plat}_set_caching_policy match _U${plat}_set_cache_size match _U${plat}_set_fpreg @@ -248,6 +251,13 @@ check_generic_unw_abi () { match _U${plat}_strerror case ${plat} in + aarch64) + match _U${plat}_is_fpreg + match _U${plat}_get_elf_image + match _U${plat}_get_exe_image_path + match _U${plat}_dwarf_search_unwind_table + match _U${plat}_dwarf_find_unwind_table + ;; arm) match _U${plat}_is_fpreg match _U${plat}_get_elf_image @@ -297,15 +307,6 @@ check_generic_unw_abi () { match _U${plat}_dwarf_search_unwind_table match _U${plat}_dwarf_find_unwind_table ;; - tilegx) - match _U${plat}_dwarf_search_unwind_table - match _U${plat}_dwarf_find_unwind_table - match _U${plat}_get_elf_image - match _U${plat}_get_exe_image_path - match _U${plat}_is_fpreg - match _U${plat}_local_addr_space_init - match ${plat}_lock - ;; s390x) match _U${plat}_is_fpreg match _U${plat}_get_elf_image diff --git a/src/native/external/libunwind/tests/flush-cache.S b/src/native/external/libunwind/tests/flush-cache.S index 3ee47269e18264..6e3a82b97ab7fa 100644 --- a/src/native/external/libunwind/tests/flush-cache.S +++ b/src/native/external/libunwind/tests/flush-cache.S @@ -77,22 +77,6 @@ flush_cache: .globl flush_cache flush_cache: bx lr -#elif defined(__tilegx__) - .text - .globl flush_cache -flush_cache: - - andi r0, r0, -64 -1: { - flush r0 ; - addi r0, r0, 64 - } - { - bgtz r1, 1b ; - addi r1, r1, -64 - } - jrp lr -#else # error Need flush_cache code for this architecture. #endif diff --git a/src/native/external/libunwind/tests/ident.c b/src/native/external/libunwind/tests/ident.c index 9024e292f5a542..082b40547f5cba 100644 --- a/src/native/external/libunwind/tests/ident.c +++ b/src/native/external/libunwind/tests/ident.c @@ -1,3 +1,27 @@ +/* libunwind - a platform-independent unwind library + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ident.h" + long f (long val) { diff --git a/src/native/external/libunwind/tests/ident.h b/src/native/external/libunwind/tests/ident.h new file mode 100644 index 00000000000000..fea6b041516c82 --- /dev/null +++ b/src/native/external/libunwind/tests/ident.h @@ -0,0 +1,28 @@ +/* libunwind - a platform-independent unwind library + * + * This file is part of libunwind. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef LIBUNWIND_TESTS_IDENT_H +#define LIBUNWIND_TESTS_IDENT_H + +extern long f (long val); + +#endif /* LIBUNWIND_TESTS_IDENT_H */ diff --git a/src/native/external/libunwind/tests/mapper.c b/src/native/external/libunwind/tests/mapper.c index b47ae780f8ede8..fcfb080792602b 100644 --- a/src/native/external/libunwind/tests/mapper.c +++ b/src/native/external/libunwind/tests/mapper.c @@ -43,6 +43,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # define MAP_NORESERVE 0 #endif +void __attribute__((noinline)) push_some_stacks(int n) +{ + if (n >= 1) + { + push_some_stacks(n - 1); + push_some_stacks(n - 1); + } +} + int main (void) { @@ -71,7 +80,7 @@ main (void) printf ("Turning on single-stepping...\n"); kill (getpid (), SIGUSR1); /* tell test-ptrace to start single-stepping */ - printf ("Va bene?\n"); + push_some_stacks (4); kill (getpid (), SIGUSR2); /* tell test-ptrace to stop single-stepping */ printf ("Turned single-stepping off...\n"); return 0; diff --git a/src/native/external/libunwind/tests/ppc64-test-plt.c b/src/native/external/libunwind/tests/ppc64-test-plt.c new file mode 100644 index 00000000000000..a8d4f3c0aa8fae --- /dev/null +++ b/src/native/external/libunwind/tests/ppc64-test-plt.c @@ -0,0 +1,164 @@ +/* + * Unittest PPC64 is_plt_entry function by inspecting output at + * different points in a mock PLT address space. + */ + +#include "dwarf.h" +#include "libunwind_i.h" + +#undef unw_get_accessors_int +unw_accessors_t *unw_get_accessors_int (unw_addr_space_t) { return NULL; } +int dwarf_step (struct dwarf_cursor*) { return 0; } +#include "ppc64/Gstep.c" + +enum +{ + ip_guard0, + ip_std, + ip_ld, + ip_mtctr, + ip_bctr, + ip_guard1, + + ip_program_end +}; + +/* Mock access_mem implementation */ +static int +access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write, + void *arg) +{ + if (write != 0) + return -1; + + const size_t mem_size = ip_program_end * sizeof(uint32_t); + const void *mem_start = arg; + const void *mem_end = (const char*) arg + mem_size; + const unw_word_t *paddr = (const unw_word_t*) addr; + + if ((void*) paddr < mem_start || (void*) paddr > mem_end) + { + return -1; + } + + *val = *paddr; + return 0; +} + +int +main () +{ + if (target_is_big_endian()) + return 77; + + const uint32_t plt_instructions[ip_program_end] = + { + 0xdeadbeef, + 0xf8410018, // std r2,24(r1) + 0xe9828730, // ld r12,-30928(r2) + 0x7d8903a6, // mtctr r12 + 0x4e800420, // bctr + 0xdeadbeef, + }; + uint32_t test_instructions[ip_program_end]; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + struct unw_addr_space mock_address_space; + mock_address_space.big_endian = 0; + mock_address_space.acc.access_mem = &access_mem; + + struct dwarf_cursor c; + c.as = &mock_address_space; + c.as_arg = &test_instructions; + + /* ip at std r2,24(r1) */ + c.ip = (unw_word_t) (test_instructions + ip_std); + if (!is_plt_entry(&c)) return -1; + + /* ld uses a different offset */ + test_instructions[ip_ld] = 0xe9820000; + if (!is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_ld is not a ld instruction */ + test_instructions[ip_ld] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_mtctr is not a mtctr instruction */ + test_instructions[ip_mtctr] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_bctr is not a bctr instruction */ + test_instructions[ip_bctr] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at ld r12,-30928(r2) */ + c.ip = (unw_word_t) (test_instructions + ip_ld); + if (!is_plt_entry(&c)) return -1; + + /* ip_std is not a std instruction */ + test_instructions[ip_std] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_mtctr is not a mtctr instruction */ + test_instructions[ip_mtctr] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_bctr is not a bctr instruction */ + test_instructions[ip_bctr] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at mtctr r12 */ + c.ip = (unw_word_t) (test_instructions + ip_mtctr); + if (!is_plt_entry(&c)) return -1; + + /* ip_std is not a std instruction */ + test_instructions[ip_std] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_ld is not a ld instruction */ + test_instructions[ip_ld] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_bctr is not a bctr instruction */ + test_instructions[ip_bctr] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at bctr */ + c.ip = (unw_word_t) (test_instructions + ip_bctr); + if (!is_plt_entry(&c)) return -1; + + /* ip_std is not a std instruction */ + test_instructions[ip_std] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_ld is not a ld instruction */ + test_instructions[ip_ld] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip_mtctr is not a mtctr instruction */ + test_instructions[ip_mtctr] = 0xf154f00d; + if (is_plt_entry(&c)) return -1; + memcpy(test_instructions, plt_instructions, sizeof(test_instructions)); + + /* ip at non-PLT instruction */ + c.ip = (unw_word_t) (test_instructions + ip_guard0); + if (is_plt_entry(&c)) return -1; + + /* ip at another non-PLT instruction */ + c.ip = (unw_word_t) (test_instructions + ip_guard1); + if (is_plt_entry(&c)) return -1; + + return 0; +} diff --git a/src/native/external/libunwind/tests/run-coredump-unwind b/src/native/external/libunwind/tests/run-coredump-unwind index 690362bdae4667..c5dc51d60ca5d3 100755 --- a/src/native/external/libunwind/tests/run-coredump-unwind +++ b/src/native/external/libunwind/tests/run-coredump-unwind @@ -20,7 +20,7 @@ add_minidebug() nm "$debuginfo" --format=posix --defined-only | awk '{ if ($2 == "T" || $2 == "t") print $1 }' | sort > "$funcsyms" # Keep all the function symbols not already in the dynamic symbol table comm -13 "$dynsyms" "$funcsyms" > "$keep_symbols" - # Copy the full debuginfo, keeping only a minumal set of symbols and removing some unnecessary sections + # Copy the full debuginfo, keeping only a minimal set of symbols and removing some unnecessary sections objcopy -S --remove-section .gdb_index --remove-section .comment --keep-symbols="$keep_symbols" "$debuginfo" "$mini_debuginfo" &> /dev/null wait #Inject the compressed data into the .gnu_debugdata section of the original binary diff --git a/src/native/external/libunwind/tests/test-async-sig.c b/src/native/external/libunwind/tests/test-async-sig.c index 2ce8b4bb711d03..d025c915106573 100644 --- a/src/native/external/libunwind/tests/test-async-sig.c +++ b/src/native/external/libunwind/tests/test-async-sig.c @@ -60,8 +60,8 @@ int sigcount; int recurcount; #endif -#define panic(args...) \ - { ++nerrors; fprintf (stderr, args); return; } +#define panic(...) \ + { ++nerrors; fprintf (stderr, __VA_ARGS__); return; } static void do_backtrace (int may_print, int get_proc_name) @@ -157,7 +157,7 @@ sighandler (int signal) printf ("SUCCESS.\n"); exit (0); } - setitimer (ITIMER_VIRTUAL, &interval, NULL); + setitimer (ITIMER_REAL, &interval, NULL); } int @@ -174,13 +174,13 @@ main (int argc, char **argv UNUSED) memset (&act, 0, sizeof (act)); act.sa_handler = sighandler; act.sa_flags = SA_SIGINFO; - sigaction (SIGVTALRM, &act, NULL); + sigaction (SIGALRM, &act, NULL); - setitimer (ITIMER_VIRTUAL, &interval, NULL); + setitimer (ITIMER_REAL, &interval, NULL); while (1) { - if (0 && verbose) + if (verbose) printf ("%s: starting backtrace\n", __FUNCTION__); do_backtrace (0, (i++ % 100) == 0); if (nerrors > nerrors_max) diff --git a/src/native/external/libunwind/tests/test-coredump-unwind.c b/src/native/external/libunwind/tests/test-coredump-unwind.c index 5c29600492d18e..6f0672a75a0882 100644 --- a/src/native/external/libunwind/tests/test-coredump-unwind.c +++ b/src/native/external/libunwind/tests/test-coredump-unwind.c @@ -326,16 +326,23 @@ main(int argc UNUSED, char **argv) if (ret < 0) error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); - if (!testcase) { - char proc_name[128]; - unw_word_t off; - unw_get_proc_name(&c, proc_name, sizeof(proc_name), &off); - - printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx %s\n", - (long) ip, - (long) pi.start_ip, (long) pi.end_ip, - (long) pi.handler, (long) pi.lsda, proc_name); - } + if (!testcase) + { + char proc_name[128]; + unw_word_t off; + unw_get_proc_name(&c, proc_name, sizeof(proc_name), &off); + + printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx %s\n", + (long) ip, + (long) pi.start_ip, (long) pi.end_ip, + (long) pi.handler, (long) pi.lsda, proc_name); + + char filename[PATH_MAX]; + unw_word_t file_offset; + ret = unw_get_elf_filename (&c, filename, sizeof (filename), &file_offset); + if (ret == UNW_ESUCCESS) + printf ("\t[%s+0x%lx]\n", filename, (long) file_offset); + } if (testcase && test_cur < TEST_FRAMES) { diff --git a/src/native/external/libunwind/tests/test-init-remote.c b/src/native/external/libunwind/tests/test-init-remote.c index 66f2d6a1e0c2bf..c5f0df40e7e9dd 100644 --- a/src/native/external/libunwind/tests/test-init-remote.c +++ b/src/native/external/libunwind/tests/test-init-remote.c @@ -39,8 +39,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#define panic(args...) \ - { fprintf (stderr, args); exit (-1); } +#define panic(...) \ + { fprintf (stderr, __VA_ARGS__); exit (-1); } int verbose; @@ -63,14 +63,19 @@ do_backtrace (void) unw_get_reg (&cursor, UNW_REG_SP, &sp); buf[0] = '\0'; if (unw_get_proc_name (&cursor, name, sizeof (name), &off) == 0) - { - if (off) - snprintf (buf, sizeof (buf), "<%s+0x%lx>", name, (long) off); - else - snprintf (buf, sizeof (buf), "<%s>", name); - } + { + if (off) + snprintf (buf, sizeof (buf), "<%s+0x%lx>", name, (long) off); + else + snprintf (buf, sizeof (buf), "<%s>", name); + } if (verbose) - printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp); + printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp); + + char filename[128]; + unw_word_t file_offset; + if (unw_get_elf_filename (&cursor,filename, sizeof (filename), &file_offset) == UNW_ESUCCESS) + printf (" [%s+0x%lx]\n", filename, (long) file_offset); ret = unw_step (&cursor); if (ret < 0) diff --git a/src/native/external/libunwind/tests/test-mem.c b/src/native/external/libunwind/tests/test-mem.c index 52c977488ac324..74089115a352b1 100644 --- a/src/native/external/libunwind/tests/test-mem.c +++ b/src/native/external/libunwind/tests/test-mem.c @@ -35,8 +35,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include -#define panic(args...) \ - { fprintf (stderr, args); exit (-1); } +#define panic(...) \ + { fprintf (stderr, __VA_ARGS__); exit (-1); } int verbose; @@ -80,23 +80,25 @@ consume_some_stack_space (void) memset (&cursor, 0, sizeof (cursor)); memset (&uc, 0, sizeof (uc)); - return sprintf (string, "hello %p %p\n", &cursor, &uc); + return sprintf (string, "hello %p %p\n", (void *)&cursor, (void *)&uc); } int main (int argc, char **argv UNUSED) { - struct rlimit rlim; + struct rlimit rlim UNUSED; verbose = argc > 1; if (consume_some_stack_space () > 9999) exit (-1); /* can't happen, but don't let the compiler know... */ +#if !defined(__QNX__) rlim.rlim_cur = 0; rlim.rlim_max = RLIM_INFINITY; setrlimit (RLIMIT_DATA, &rlim); setrlimit (RLIMIT_AS, &rlim); +#endif /* !defined(__QNX__) */ do_backtrace (); return 0; diff --git a/src/native/external/libunwind/tests/test-proc-info.c b/src/native/external/libunwind/tests/test-proc-info.c index c4145bc374ec38..408d80e25fa92c 100644 --- a/src/native/external/libunwind/tests/test-proc-info.c +++ b/src/native/external/libunwind/tests/test-proc-info.c @@ -40,8 +40,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ int errors; -#define panic(args...) \ - { ++errors; fprintf (stderr, args); return -1; } +#define panic(...) \ + { ++errors; fprintf (stderr, __VA_ARGS__); return -1; } static int find_proc_info (unw_addr_space_t as UNUSED, diff --git a/src/native/external/libunwind/tests/test-ptrace-misc.c b/src/native/external/libunwind/tests/test-ptrace-misc.c index 374059dc44a972..cbb9557ee5c431 100644 --- a/src/native/external/libunwind/tests/test-ptrace-misc.c +++ b/src/native/external/libunwind/tests/test-ptrace-misc.c @@ -33,6 +33,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include +#include "ident.h" + pid_t self; int global[64]; @@ -68,7 +70,6 @@ func (int arg) int bar (int v) { - extern long f (long); int arr[1] = { v }; uintptr_t r; diff --git a/src/native/external/libunwind/tests/test-ptrace.c b/src/native/external/libunwind/tests/test-ptrace.c index 846bcd80079b40..bfd45bf9aafb98 100644 --- a/src/native/external/libunwind/tests/test-ptrace.c +++ b/src/native/external/libunwind/tests/test-ptrace.c @@ -55,6 +55,7 @@ static const int nerrors_max = 100; int nerrors; int verbose; int print_names = 1; +int print_elf_filename; enum { @@ -64,8 +65,8 @@ enum } trace_mode = SYSCALL; -#define panic(args...) \ - do { fprintf (stderr, args); ++nerrors; } while (0) +#define panic(...) \ + do { fprintf (stderr, __VA_ARGS__); ++nerrors; } while (0) static unw_addr_space_t as; static struct UPT_info *ui; @@ -111,7 +112,7 @@ do_backtrace (void) printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp); } - if ((ret = unw_get_proc_info (&c, &pi)) < 0) + if ((ret = unw_get_proc_info (&c, &pi)) < 0 && ret != -UNW_ENOINFO) /* It's possible unw_get_proc_info don't return information */ panic ("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); else if (verbose) printf ("\tproc=%016lx-%016lx\n\thandler=%lx lsda=%lx", @@ -131,6 +132,14 @@ do_backtrace (void) if (verbose) printf ("\n"); + if (print_elf_filename) + { + if ((ret = unw_get_elf_filename(&c, buf, sizeof (buf), &off)) != UNW_ESUCCESS) + panic ("unw_get_elf_filename(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); + else if (verbose) + printf ("\t[%s+0x%lx]\n", buf, (long) off); + } + ret = unw_step (&c); if (ret < 0) { @@ -178,8 +187,11 @@ main (int argc, char **argv) if (argc == 1) { +#ifdef HAVE_EXECVPE static char *args[] = { "self", "ls", "/", NULL }; - +#else + static char *args[] = { "self", "/bin/ls", "/", NULL }; +#endif /* automated test case */ argv = args; @@ -207,6 +219,9 @@ main (int argc, char **argv) else if (strcmp (argv[optind], "-n") == 0) /* Don't look-up and print symbol names. */ ++optind, print_names = 0; + else if (strcmp (argv[optind], "-f") == 0) + /* Print elf filenames. */ + ++optind, print_elf_filename = 1; else fprintf(stderr, "unrecognized option: %s\n", argv[optind++]); if (optind >= argc) @@ -222,21 +237,29 @@ main (int argc, char **argv) dup2 (open ("/dev/null", O_WRONLY), 1); #if HAVE_DECL_PTRACE_TRACEME - ptrace (PTRACE_TRACEME, 0, 0, 0); + long stat = ptrace (PTRACE_TRACEME, 0, 0, 0); #elif HAVE_DECL_PT_TRACE_ME - ptrace (PT_TRACE_ME, 0, 0, 0); + int stat = ptrace (PT_TRACE_ME, 0, 0, 0); #else #error Trace me #endif + if (stat == -1) + { + if (verbose) + { + fprintf(stderr, "ptrace() returned %ld errno=%d (%s)\n", (long)stat, errno, strerror(errno)); + } + _exit(77); + } if ((argc > 1) && (optind == argc)) { fprintf(stderr, "Need to specify a command line for the child\n"); exit (-1); } -#ifdef __FreeBSD__ - execve (argv[optind], argv + optind, environ); -#else +#ifdef HAVE_EXECVPE execvpe (argv[optind], argv + optind, environ); +#else + execve (argv[optind], argv + optind, environ); #endif _exit (-1); } @@ -262,6 +285,8 @@ main (int argc, char **argv) { if (WEXITSTATUS (status) != 0) panic ("child's exit status %d\n", WEXITSTATUS (status)); + if (WEXITSTATUS (status) == 77) + _exit(77); break; } else if (WIFSIGNALED (status)) @@ -333,10 +358,10 @@ main (int argc, char **argv) if (!state) do_backtrace (); state ^= 1; -#if HAVE_DECL_PTRACE_SYSCALL - ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig); -#elif HAVE_DECL_PT_SYSCALL +#if HAVE_DECL_PT_SYSCALL ptrace (PT_SYSCALL, target_pid, (caddr_t)1, pending_sig); +#elif HAVE_DECL_PTRACE_SYSCALL + ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig); #else #error Syscall me #endif diff --git a/src/native/external/libunwind/tests/test-reg-state.c b/src/native/external/libunwind/tests/test-reg-state.c index ac713ea68ad721..4406acb955a2f7 100644 --- a/src/native/external/libunwind/tests/test-reg-state.c +++ b/src/native/external/libunwind/tests/test-reg-state.c @@ -36,8 +36,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include -#define panic(args...) \ - { fprintf (stderr, args); exit (-1); } +#define panic(...) \ + { fprintf (stderr, __VA_ARGS__); exit (-1); } int verbose; @@ -111,7 +111,7 @@ consume_some_stack_space (void) memset (&cursor, 0, sizeof (cursor)); memset (&uc, 0, sizeof (uc)); - return sprintf (string, "hello %p %p\n", &cursor, &uc); + return sprintf (string, "hello %p %p\n", (void *)&cursor, (void *)&uc); } int @@ -124,9 +124,11 @@ main (int argc, char **argv UNUSED) if (consume_some_stack_space () > 9999) exit (-1); /* can't happen, but don't let the compiler know... */ +#if !defined(__QNX__) rlim.rlim_cur = 0; rlim.rlim_max = RLIM_INFINITY; setrlimit (RLIMIT_DATA, &rlim); +#endif /* !defined(__QNX__) */ do_backtrace (); return 0; diff --git a/src/native/external/libunwind/tests/test-setjmp.c b/src/native/external/libunwind/tests/test-setjmp.c index 59d1c5a083b309..d2468ff05f6c87 100644 --- a/src/native/external/libunwind/tests/test-setjmp.c +++ b/src/native/external/libunwind/tests/test-setjmp.c @@ -1,6 +1,6 @@ /* libunwind - a platform-independent unwind library Copyright (C) 2003 Hewlett-Packard Co - Contributed by David Mosberger-Tang + Contributed by David Mosberger-Tang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -39,7 +39,7 @@ static jmp_buf jbuf; static sigjmp_buf sigjbuf; static sigset_t sigset4; -void +NORETURN void raise_longjmp (jmp_buf jbuf, int i, int n) { while (i < n) @@ -58,32 +58,32 @@ test_setjmp (void) for (i = 0; i < 10; ++i) { if ((ret = setjmp (jbuf))) - { - if (verbose) - printf ("%s: secondary setjmp () return, ret=%d\n", - __FUNCTION__, ret); - if (ret != i + 1) - { - fprintf (stderr, "%s: setjmp() returned %d, expected %d\n", - __FUNCTION__, ret, i + 1); - ++nerrors; - } - continue; - } + { + if (verbose) + printf ("%s: secondary setjmp () return, ret=%d\n", + __FUNCTION__, ret); + if (ret != i + 1) + { + fprintf (stderr, "%s: setjmp() returned %d, expected %d\n", + __FUNCTION__, ret, i + 1); + ++nerrors; + } + continue; + } if (verbose) - printf ("%s.%d: done with setjmp(); calling children\n", - __FUNCTION__, i + 1); + printf ("%s.%d: done with setjmp(); calling children\n", + __FUNCTION__, i + 1); raise_longjmp (jbuf, 0, i + 1); fprintf (stderr, "%s: raise_longjmp() returned unexpectedly\n", - __FUNCTION__); + __FUNCTION__); ++nerrors; } } -void +NORETURN void raise_siglongjmp (sigjmp_buf jbuf, int i, int n) { while (i < n) @@ -102,26 +102,26 @@ test_sigsetjmp (void) for (i = 0; i < 10; ++i) { if ((ret = sigsetjmp (jbuf, 1))) - { - if (verbose) - printf ("%s: secondary sigsetjmp () return, ret=%d\n", - __FUNCTION__, ret); - if (ret != i + 1) - { - fprintf (stderr, "%s: sigsetjmp() returned %d, expected %d\n", - __FUNCTION__, ret, i + 1); - ++nerrors; - } - continue; - } + { + if (verbose) + printf ("%s: secondary sigsetjmp () return, ret=%d\n", + __FUNCTION__, ret); + if (ret != i + 1) + { + fprintf (stderr, "%s: sigsetjmp() returned %d, expected %d\n", + __FUNCTION__, ret, i + 1); + ++nerrors; + } + continue; + } if (verbose) - printf ("%s.%d: done with sigsetjmp(); calling children\n", - __FUNCTION__, i + 1); + printf ("%s.%d: done with sigsetjmp(); calling children\n", + __FUNCTION__, i + 1); raise_siglongjmp (jbuf, 0, i + 1); fprintf (stderr, "%s: raise_siglongjmp() returned unexpectedly\n", - __FUNCTION__); + __FUNCTION__); ++nerrors; } } @@ -143,7 +143,7 @@ sighandler (int signal) int main (int argc, char **argv UNUSED) { - volatile sigset_t sigset1, sigset2, sigset3; + sigset_t sigset1, sigset2, sigset3; volatile struct sigaction act; if (argc > 1) @@ -172,13 +172,13 @@ main (int argc, char **argv UNUSED) sigemptyset ((sigset_t *) &sigset3); sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset2, - sizeof (sigset_t)) != 0) - { - fprintf (stderr, "FAILURE: _longjmp() manipulated signal mask!\n"); - ++nerrors; - } + sizeof (sigset_t)) != 0) + { + fprintf (stderr, "FAILURE: _longjmp() manipulated signal mask!\n"); + ++nerrors; + } else if (verbose) - printf ("OK: _longjmp() seems not to change signal mask\n"); + printf ("OK: _longjmp() seems not to change signal mask\n"); } else { @@ -193,14 +193,14 @@ main (int argc, char **argv UNUSED) sigemptyset ((sigset_t *) &sigset3); sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset1, - sizeof (sigset_t)) != 0) - { - fprintf (stderr, - "FAILURE: siglongjmp() didn't restore signal mask!\n"); - ++nerrors; - } + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() didn't restore signal mask!\n"); + ++nerrors; + } else if (verbose) - printf ("OK: siglongjmp() restores signal mask when asked to\n"); + printf ("OK: siglongjmp() restores signal mask when asked to\n"); } else { @@ -215,14 +215,14 @@ main (int argc, char **argv UNUSED) sigemptyset ((sigset_t *) &sigset3); sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset2, - sizeof (sigset_t)) != 0) - { - fprintf (stderr, - "FAILURE: siglongjmp() changed signal mask!\n"); - ++nerrors; - } + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() changed signal mask!\n"); + ++nerrors; + } else if (verbose) - printf ("OK: siglongjmp() leaves signal mask alone when asked to\n"); + printf ("OK: siglongjmp() leaves signal mask alone when asked to\n"); } else { @@ -237,14 +237,14 @@ main (int argc, char **argv UNUSED) sigemptyset ((sigset_t *) &sigset3); sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset1, - sizeof (sigset_t)) != 0) - { - fprintf (stderr, - "FAILURE: siglongjmp() didn't restore signal mask!\n"); - ++nerrors; - } + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() didn't restore signal mask!\n"); + ++nerrors; + } else if (verbose) - printf ("OK: siglongjmp() restores signal mask when asked to\n"); + printf ("OK: siglongjmp() restores signal mask when asked to\n"); } else { @@ -261,14 +261,14 @@ main (int argc, char **argv UNUSED) sigemptyset ((sigset_t *) &sigset3); sigprocmask (SIG_BLOCK, NULL, (sigset_t *) &sigset3); if (memcmp ((sigset_t *) &sigset3, (sigset_t *) &sigset4, - sizeof (sigset_t)) != 0) - { - fprintf (stderr, - "FAILURE: siglongjmp() changed signal mask!\n"); - ++nerrors; - } + sizeof (sigset_t)) != 0) + { + fprintf (stderr, + "FAILURE: siglongjmp() changed signal mask!\n"); + ++nerrors; + } else if (verbose) - printf ("OK: siglongjmp() leaves signal mask alone when asked to\n"); + printf ("OK: siglongjmp() leaves signal mask alone when asked to\n"); } else { diff --git a/src/native/external/libunwind/tests/x64-test-dwarf-expressions.S b/src/native/external/libunwind/tests/x64-test-dwarf-expressions.S index 19ebc9d438d990..c6409b7c7928d1 100644 --- a/src/native/external/libunwind/tests/x64-test-dwarf-expressions.S +++ b/src/native/external/libunwind/tests/x64-test-dwarf-expressions.S @@ -76,3 +76,6 @@ DW_CFA_expression_inner: ret .cfi_endproc .size DW_CFA_expression_inner,.-DW_CFA_expression_inner + + /* We do not need executable stack. */ + .section .note.GNU-stack,"",@progbits diff --git a/src/native/external/libunwind/tests/x64-unwind-badjmp-signal-frame.c b/src/native/external/libunwind/tests/x64-unwind-badjmp-signal-frame.c index c7b7cf7335a8bd..1c3f8f3653968b 100644 --- a/src/native/external/libunwind/tests/x64-unwind-badjmp-signal-frame.c +++ b/src/native/external/libunwind/tests/x64-unwind-badjmp-signal-frame.c @@ -26,9 +26,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include -#include #include -#include +#include #include #ifdef HAVE_SYS_PTRACE_H @@ -37,12 +36,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define UNW_LOCAL_ONLY #include +#include "compiler.h" /* * unwind in the signal handler checking the backtrace is correct * after a bad jump. */ -void handle_sigsegv(int signal, siginfo_t *info, void *ucontext) +void handle_sigsegv(int signal UNUSED, siginfo_t *info UNUSED, void *ucontext UNUSED) { /* * 0 = success @@ -57,6 +57,9 @@ void handle_sigsegv(int signal, siginfo_t *info, void *ucontext) int found_signal_frame = 0; int i = 0; char *names[] = { +#if defined __FreeBSD__ + "", +#endif "", "main", }; @@ -105,7 +108,7 @@ void handle_sigsegv(int signal, siginfo_t *info, void *ucontext) void (*invalid_function)() = (void*)1; -int main(int argc, char *argv[]) +int main(int argc UNUSED, char *argv[] UNUSED) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); diff --git a/src/native/external/libunwind_extras/CMakeLists.txt b/src/native/external/libunwind_extras/CMakeLists.txt index d40b920e0b135e..07a4bb64db940d 100644 --- a/src/native/external/libunwind_extras/CMakeLists.txt +++ b/src/native/external/libunwind_extras/CMakeLists.txt @@ -9,6 +9,7 @@ add_definitions(-DHAVE___THREAD=0) add_definitions(-D_GNU_SOURCE) add_definitions(-DPACKAGE_STRING="") add_definitions(-DPACKAGE_BUGREPORT="") +add_definitions(-DHAVE_DL_ITERATE_PHDR=1) if(CLR_CMAKE_HOST_UNIX) if (CLR_CMAKE_HOST_ARCH_AMD64) diff --git a/src/native/external/libunwind_extras/config.h.in b/src/native/external/libunwind_extras/config.h.in index 4c06e69879688c..35b44b11d80726 100644 --- a/src/native/external/libunwind_extras/config.h.in +++ b/src/native/external/libunwind_extras/config.h.in @@ -20,6 +20,8 @@ #cmakedefine HAVE_STDALIGN_H #cmakedefine HAVE_STDALIGN_ALIGNAS +#cmakedefine HAVE_PIPE2 + #if defined(_MSC_VER) && defined(HAVE_STDALIGN_H) && !defined(HAVE_STDALIGN_ALIGNAS) // alignment is a performance optimization for the cross compile libunwind // Simply ignore it if it is not supported by the compiler diff --git a/src/native/external/libunwind_extras/configure.cmake b/src/native/external/libunwind_extras/configure.cmake index deb2bd5fe14114..711f64f040bce3 100644 --- a/src/native/external/libunwind_extras/configure.cmake +++ b/src/native/external/libunwind_extras/configure.cmake @@ -1,5 +1,6 @@ include(CheckCSourceCompiles) include(CheckIncludeFiles) +include(CheckFunctionExists) if(CLR_CMAKE_HOST_WIN32) # Our posix abstraction layer will provide these headers @@ -37,6 +38,8 @@ check_include_files(sys/link.h HAVE_SYS_LINK_H) check_include_files(atomic_ops.h HAVE_ATOMIC_OPS_H) +check_function_exists(pipe2 HAVE_PIPE2) + check_c_source_compiles(" int main(int argc, char **argv) { From d370e8afb95bf9fc66759681a132f649bafb6eb9 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 19 Jan 2024 11:28:53 -0500 Subject: [PATCH 126/189] Remove DER attempt when reading machine certificates Reading the machine certificates as DER did not work in the first place because the BIO was not rewound between the PEM and DER attempt. Since no one noticed DER never worked, remove it. --- .../X509Certificates/OpenSslCachedSystemStoreProvider.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs index e66b3d1ad11022..409170a40f2656 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCachedSystemStoreProvider.cs @@ -232,8 +232,7 @@ bool ProcessFile(string file, out DateTime lastModified, bool skipStat = false) // Because we don't validate for a specific usage, derived certificates are rejected. // For now, we skip the certificates with AUX data and use the regular certificates. ICertificatePal? pal; - while (OpenSslX509CertificateReader.TryReadX509PemNoAux(fileBio, out pal) || - OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal)) + while (OpenSslX509CertificateReader.TryReadX509PemNoAux(fileBio, out pal)) { readData = true; X509Certificate2 cert = new X509Certificate2(pal); From 3d4a82b38499f0ac26b850dbdced2bb535ac499f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 19 Jan 2024 08:29:12 -0800 Subject: [PATCH 127/189] Move ComLibrary test projects for host tests to pre-built assets (#97185) --- .../ComLibrary/ComLibrary.cs | 0 .../ComLibrary/ComLibrary.csproj | 3 +- .../ComLibraryConflictingGuid.cs | 0 .../ComLibraryConflictingGuid.csproj | 1 - .../ComLibraryMissingGuid.cs | 0 .../ComLibraryMissingGuid.csproj | 1 - .../NativeHosting/Comhost.cs | 108 +++++++----------- .../NativeHosting/ComhostSideBySide.cs | 20 ++-- .../ClsidMapTests.cs | 64 ++++------- 9 files changed, 78 insertions(+), 119 deletions(-) rename src/installer/tests/Assets/{TestProjects => Projects}/ComLibrary/ComLibrary.cs (100%) rename src/installer/tests/Assets/{TestProjects => Projects}/ComLibrary/ComLibrary.csproj (63%) rename src/installer/tests/Assets/{TestProjects => Projects}/ComLibraryConflictingGuid/ComLibraryConflictingGuid.cs (100%) rename src/installer/tests/Assets/{TestProjects => Projects}/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj (67%) rename src/installer/tests/Assets/{TestProjects => Projects}/ComLibraryMissingGuid/ComLibraryMissingGuid.cs (100%) rename src/installer/tests/Assets/{TestProjects => Projects}/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj (67%) diff --git a/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.cs b/src/installer/tests/Assets/Projects/ComLibrary/ComLibrary.cs similarity index 100% rename from src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.cs rename to src/installer/tests/Assets/Projects/ComLibrary/ComLibrary.cs diff --git a/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.csproj b/src/installer/tests/Assets/Projects/ComLibrary/ComLibrary.csproj similarity index 63% rename from src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.csproj rename to src/installer/tests/Assets/Projects/ComLibrary/ComLibrary.csproj index 27861faa61056d..2e38ad45709bf0 100644 --- a/src/installer/tests/Assets/TestProjects/ComLibrary/ComLibrary.csproj +++ b/src/installer/tests/Assets/Projects/ComLibrary/ComLibrary.csproj @@ -2,8 +2,9 @@ $(NetCoreAppCurrent) - $(MNAVersion) true + + 1.0.0 diff --git a/src/installer/tests/Assets/TestProjects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.cs b/src/installer/tests/Assets/Projects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.cs similarity index 100% rename from src/installer/tests/Assets/TestProjects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.cs rename to src/installer/tests/Assets/Projects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.cs diff --git a/src/installer/tests/Assets/TestProjects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj b/src/installer/tests/Assets/Projects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj similarity index 67% rename from src/installer/tests/Assets/TestProjects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj rename to src/installer/tests/Assets/Projects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj index 637cbf46c656dd..77439bfe0c2fe6 100644 --- a/src/installer/tests/Assets/TestProjects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj +++ b/src/installer/tests/Assets/Projects/ComLibraryConflictingGuid/ComLibraryConflictingGuid.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent) - $(MNAVersion) diff --git a/src/installer/tests/Assets/TestProjects/ComLibraryMissingGuid/ComLibraryMissingGuid.cs b/src/installer/tests/Assets/Projects/ComLibraryMissingGuid/ComLibraryMissingGuid.cs similarity index 100% rename from src/installer/tests/Assets/TestProjects/ComLibraryMissingGuid/ComLibraryMissingGuid.cs rename to src/installer/tests/Assets/Projects/ComLibraryMissingGuid/ComLibraryMissingGuid.cs diff --git a/src/installer/tests/Assets/TestProjects/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj b/src/installer/tests/Assets/Projects/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj similarity index 67% rename from src/installer/tests/Assets/TestProjects/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj rename to src/installer/tests/Assets/Projects/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj index 637cbf46c656dd..77439bfe0c2fe6 100644 --- a/src/installer/tests/Assets/TestProjects/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj +++ b/src/installer/tests/Assets/Projects/ComLibraryMissingGuid/ComLibraryMissingGuid.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent) - $(MNAVersion) diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs index 4d023023d9791f..3d5b902ff1ad4f 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/Comhost.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; -using System.Runtime.InteropServices; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.NET.HostModel.ComHost; @@ -36,12 +35,12 @@ public void ActivateClass(int count, bool synchronous) sharedState.ComHostPath, sharedState.ClsidString }; - CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.ComLibraryFixture.BuiltDotnet.BinPath) + CommandResult result = sharedState.CreateNativeHostCommand(args, TestContext.BuiltDotNet.BinPath) .Execute(); result.Should().Pass() .And.HaveStdOutContaining("New instance of Server created") - .And.ExecuteInIsolatedContext(sharedState.ComLibraryFixture.TestProject.AssemblyName); + .And.ExecuteInIsolatedContext(sharedState.ComLibrary.AssemblyName); for (var i = 1; i <= count; ++i) { @@ -54,13 +53,11 @@ public void ActivateClass(int count, bool synchronous) [InlineData(false)] public void ActivateClass_ContextConfig(bool inDefaultContext) { - using (var fixture = sharedState.ComLibraryFixture.Copy()) + using (var library = sharedState.ComLibrary.Copy()) { - var comHost = Path.Combine( - fixture.TestProject.BuiltApp.Location, - $"{fixture.TestProject.AssemblyName}.comhost.dll"); + var comHost = Path.Combine(library.Location, $"{library.AssemblyName}.comhost.dll"); - RuntimeConfig.FromFile(fixture.TestProject.RuntimeConfigJson) + RuntimeConfig.FromFile(library.RuntimeConfigJson) .WithProperty("System.Runtime.InteropServices.COM.LoadComponentInDefaultContext", inDefaultContext.ToString()) .Save(); @@ -71,7 +68,7 @@ public void ActivateClass_ContextConfig(bool inDefaultContext) comHost, sharedState.ClsidString }; - CommandResult result = sharedState.CreateNativeHostCommand(args, fixture.BuiltDotnet.BinPath) + CommandResult result = sharedState.CreateNativeHostCommand(args, TestContext.BuiltDotNet.BinPath) .Execute(); result.Should().Pass() @@ -80,11 +77,11 @@ public void ActivateClass_ContextConfig(bool inDefaultContext) if (inDefaultContext) { - result.Should().ExecuteInDefaultContext(sharedState.ComLibraryFixture.TestProject.AssemblyName); + result.Should().ExecuteInDefaultContext(library.AssemblyName); } else { - result.Should().ExecuteInIsolatedContext(sharedState.ComLibraryFixture.TestProject.AssemblyName); + result.Should().ExecuteInIsolatedContext(library.AssemblyName); } } } @@ -92,12 +89,10 @@ public void ActivateClass_ContextConfig(bool inDefaultContext) [Fact] public void ActivateClass_IgnoreAppLocalHostFxr() { - using (var fixture = sharedState.ComLibraryFixture.Copy()) + using (var library = sharedState.ComLibrary.Copy()) { - File.WriteAllText(Path.Combine(fixture.TestProject.BuiltApp.Location, "hostfxr.dll"), string.Empty); - var comHostWithAppLocalFxr = Path.Combine( - fixture.TestProject.BuiltApp.Location, - $"{ fixture.TestProject.AssemblyName }.comhost.dll"); + File.WriteAllText(Path.Combine(library.Location, Binaries.HostFxr.FileName), string.Empty); + var comHostWithAppLocalFxr = Path.Combine(library.Location, $"{library.AssemblyName}.comhost.dll"); string[] args = { "comhost", @@ -105,8 +100,8 @@ public void ActivateClass_IgnoreAppLocalHostFxr() "1", comHostWithAppLocalFxr, sharedState.ClsidString - }; - CommandResult result = sharedState.CreateNativeHostCommand(args, fixture.BuiltDotnet.BinPath) + }; + CommandResult result = sharedState.CreateNativeHostCommand(args, TestContext.BuiltDotNet.BinPath) .Execute(); result.Should().Pass() @@ -119,16 +114,10 @@ public void ActivateClass_IgnoreAppLocalHostFxr() [Fact] public void ActivateClass_ValidateIErrorInfoResult() { - using (var fixture = sharedState.ComLibraryFixture.Copy()) + using (var library = sharedState.ComLibrary.Copy()) { - string missingRuntimeConfig = Path.Combine(fixture.TestProject.BuiltApp.Location, - $"{ fixture.TestProject.AssemblyName }.runtimeconfig.json"); - - File.Delete(missingRuntimeConfig); - - var comHost = Path.Combine( - fixture.TestProject.BuiltApp.Location, - $"{ fixture.TestProject.AssemblyName }.comhost.dll"); + File.Delete(library.RuntimeConfigJson); + var comHost = Path.Combine(library.Location, $"{library.AssemblyName}.comhost.dll"); string[] args = { "comhost", @@ -137,38 +126,31 @@ public void ActivateClass_ValidateIErrorInfoResult() comHost, sharedState.ClsidString }; - CommandResult result = sharedState.CreateNativeHostCommand(args, fixture.BuiltDotnet.BinPath) + CommandResult result = sharedState.CreateNativeHostCommand(args, TestContext.BuiltDotNet.BinPath) .Execute(); result.Should().Pass() - .And.HaveStdOutContaining($"The specified runtimeconfig.json [{missingRuntimeConfig}] does not exist"); + .And.HaveStdOutContaining($"The specified runtimeconfig.json [{library.RuntimeConfigJson}] does not exist"); } } [Fact] public void LoadTypeLibraries() { - using (var fixture = sharedState.ComLibraryFixture.Copy()) - { - var comHost = Path.Combine( - fixture.TestProject.BuiltApp.Location, - $"{ fixture.TestProject.AssemblyName }.comhost.dll"); - - string[] args = { - "comhost", - "typelib", - "2", - comHost, - sharedState.ClsidString - }; - CommandResult result = sharedState.CreateNativeHostCommand(args, fixture.BuiltDotnet.BinPath) - .Execute(); + string[] args = { + "comhost", + "typelib", + "2", + sharedState.ComHostPath, + sharedState.ClsidString + }; + CommandResult result = sharedState.CreateNativeHostCommand(args, TestContext.BuiltDotNet.BinPath) + .Execute(); - result.Should().Pass() - .And.HaveStdOutContaining("Loading default type library succeeded.") - .And.HaveStdOutContaining("Loading type library 1 succeeded.") - .And.HaveStdOutContaining("Loading type library 2 succeeded."); - } + result.Should().Pass() + .And.HaveStdOutContaining("Loading default type library succeeded.") + .And.HaveStdOutContaining("Loading type library 1 succeeded.") + .And.HaveStdOutContaining("Loading type library 2 succeeded."); } public class SharedTestState : SharedTestStateBase @@ -176,7 +158,8 @@ public class SharedTestState : SharedTestStateBase public string ComHostPath { get; } public string ClsidString { get; } = "{438968CE-5950-4FBC-90B0-E64691350DF5}"; - public TestProjectFixture ComLibraryFixture { get; } + + public TestApp ComLibrary { get; } public string ClsidMapPath { get; } @@ -190,13 +173,11 @@ public SharedTestState() return; } - ComLibraryFixture = new TestProjectFixture("ComLibrary", RepoDirectories) - .EnsureRestored() - .BuildProject(); + ComLibrary = TestApp.CreateFromBuiltAssets("ComLibrary"); // Create a .clsidmap from the assembly - ClsidMapPath = Path.Combine(BaseDirectory, $"{ ComLibraryFixture.TestProject.AssemblyName }.clsidmap"); - using (var assemblyStream = new FileStream(ComLibraryFixture.TestProject.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) + ClsidMapPath = Path.Combine(BaseDirectory, $"{ComLibrary.AssemblyName}.clsidmap"); + using (var assemblyStream = new FileStream(ComLibrary.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) using (var peReader = new System.Reflection.PortableExecutable.PEReader(assemblyStream)) { if (peReader.HasMetadata) @@ -206,20 +187,17 @@ public SharedTestState() } } - // Use the locally built comhost to create a comhost with the embedded .clsidmap - ComHostPath = Path.Combine( - ComLibraryFixture.TestProject.BuiltApp.Location, - $"{ ComLibraryFixture.TestProject.AssemblyName }.comhost.dll"); - // Include the test type libraries in the ComHost tests. TypeLibraries = new Dictionary { - { 1, Path.Combine(RepoDirectories.HostTestArtifacts, "Server.tlb") }, - { 2, Path.Combine(RepoDirectories.HostTestArtifacts, "Nested.tlb") } + { 1, Path.Combine(RepoDirectoriesProvider.Default.HostTestArtifacts, "Server.tlb") }, + { 2, Path.Combine(RepoDirectoriesProvider.Default.HostTestArtifacts, "Nested.tlb") } }; + // Use the locally built comhost to create a comhost with the embedded .clsidmap and type libraries + ComHostPath = Path.Combine(ComLibrary.Location, $"{ComLibrary.AssemblyName}.comhost.dll"); ComHost.Create( - Path.Combine(RepoDirectories.HostArtifacts, "comhost.dll"), + Path.Combine(RepoDirectoriesProvider.Default.HostArtifacts, "comhost.dll"), ComHostPath, ClsidMapPath, TypeLibraries); @@ -227,9 +205,7 @@ public SharedTestState() protected override void Dispose(bool disposing) { - if (ComLibraryFixture != null) - ComLibraryFixture.Dispose(); - + ComLibrary?.Dispose(); base.Dispose(disposing); } } diff --git a/src/installer/tests/HostActivation.Tests/NativeHosting/ComhostSideBySide.cs b/src/installer/tests/HostActivation.Tests/NativeHosting/ComhostSideBySide.cs index e2e55d1fc9df30..ad8f6d2a6f255e 100644 --- a/src/installer/tests/HostActivation.Tests/NativeHosting/ComhostSideBySide.cs +++ b/src/installer/tests/HostActivation.Tests/NativeHosting/ComhostSideBySide.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; -using System.Runtime.InteropServices; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.NET.HostModel.ComHost; @@ -33,7 +31,7 @@ public void ActivateClass() CommandResult result = Command.Create(sharedState.ComSxsPath, args) .EnableTracingAndCaptureOutputs() - .DotNetRoot(sharedState.ComLibraryFixture.BuiltDotnet.BinPath) + .DotNetRoot(TestContext.BuiltDotNet.BinPath) .MultilevelLookup(false) .Execute(); @@ -51,7 +49,7 @@ public void LocateEmbeddedTlb() CommandResult result = Command.Create(sharedState.ComSxsPath, args) .EnableTracingAndCaptureOutputs() - .DotNetRoot(sharedState.ComLibraryFixture.BuiltDotnet.BinPath) + .DotNetRoot(TestContext.BuiltDotNet.BinPath) .MultilevelLookup(false) .Execute(); @@ -99,16 +97,16 @@ public SharedTestState() } string comsxsDirectory = BaseDirectory; - string regFreeManifestName = $"{ ComLibraryFixture.TestProject.AssemblyName }.X.manifest"; + string regFreeManifestName = $"{ ComLibrary.AssemblyName }.X.manifest"; string regFreeManifestPath = Path.Combine(comsxsDirectory, regFreeManifestName); - using (var assemblyStream = new FileStream(ComLibraryFixture.TestProject.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) + using (var assemblyStream = new FileStream(ComLibrary.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) using (var peReader = new System.Reflection.PortableExecutable.PEReader(assemblyStream)) { if (peReader.HasMetadata) { MetadataReader reader = peReader.GetMetadataReader(); RegFreeComManifest.CreateManifestFromClsidmap( - ComLibraryFixture.TestProject.AssemblyName, + ComLibrary.AssemblyName, Path.GetFileName(ComHostPath), reader.GetAssemblyDefinition().Version.ToString(), ClsidMapPath, @@ -121,7 +119,7 @@ public SharedTestState() string comsxsName = Binaries.GetExeFileNameForCurrentPlatform("comsxs"); ComSxsPath = Path.Combine(comsxsDirectory, comsxsName); File.Copy( - Path.Combine(RepoDirectories.HostTestArtifacts, comsxsName), + Path.Combine(RepoDirectoriesProvider.Default.HostTestArtifacts, comsxsName), ComSxsPath); ManagedHostFixture_FrameworkDependent = new TestProjectFixture("ManagedHost", RepoDirectories) @@ -136,9 +134,9 @@ public SharedTestState() // Copy the ComLibrary output and comhost to the ComSxS and ManagedHost directories string[] toCopy = { - ComLibraryFixture.TestProject.AppDll, - ComLibraryFixture.TestProject.DepsJson, - ComLibraryFixture.TestProject.RuntimeConfigJson, + ComLibrary.AppDll, + ComLibrary.DepsJson, + ComLibrary.RuntimeConfigJson, ComHostPath, }; foreach (string filePath in toCopy) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs index 8b496c191f222c..4cfa179ee605c5 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs @@ -1,15 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.CoreSetup.Test; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; -using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; -using System.Text; + +using Microsoft.DotNet.CoreSetup.Test; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.NET.HostModel.ComHost.Tests @@ -26,25 +25,25 @@ public ClsidMapTests(SharedTestState fixture) [Fact] public void PublicComVisibleTypeWithGuidAdded() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleGuid); Assert.NotNull(comVisibleEntry); JObject entry = (JObject)comVisibleEntry.Value; Assert.Equal(SharedTestState.ComVisibleTypeName, entry.Property("type").Value.ToString()); - Assert.Equal(SharedTestState.ComVisibleAssemblyName, entry.Property("assembly").Value.ToString()); + Assert.Equal(System.Reflection.AssemblyName.GetAssemblyName(sharedTestState.ComLibrary.AppDll).FullName, entry.Property("assembly").Value.ToString()); } [Fact] public void PublicComVisibleTypeWithoutGuidThrows() { - var exception = Assert.Throws(() => CreateClsidMap(sharedTestState.ComLibraryMissingGuidFixture)); + var exception = Assert.Throws(() => CreateClsidMap(sharedTestState.ComLibraryMissingGuid)); Assert.Equal(SharedTestState.MissingGuidTypeName, exception.TypeName); } [Fact] public void PublicNestedTypeOfPublicTypeAdded() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleNestedGuid); Assert.NotNull(comVisibleEntry); JObject entry = (JObject)comVisibleEntry.Value; @@ -54,7 +53,7 @@ public void PublicNestedTypeOfPublicTypeAdded() [Fact] public void NonPublicTypeNotAdded() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleNonPublicGuid); Assert.Null(comVisibleEntry); } @@ -62,7 +61,7 @@ public void NonPublicTypeNotAdded() [Fact] public void PublicNestedTypeOfNonPublicTypeNotAdded() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleNonPublicNestedGuid); Assert.Null(comVisibleEntry); } @@ -70,7 +69,7 @@ public void PublicNestedTypeOfNonPublicTypeNotAdded() [Fact] public void PublicComVisibleTypeWithDuplicateGuidThrows() { - var exception = Assert.Throws(() => CreateClsidMap(sharedTestState.ComLibraryConflictingGuidFixture)); + var exception = Assert.Throws(() => CreateClsidMap(sharedTestState.ComLibraryConflictingGuid)); Assert.Equal(Guid.Parse(SharedTestState.ConflictingGuid), exception.Guid); Assert.Equal(SharedTestState.ConflictingGuidTypeName1, exception.TypeName1); Assert.Equal(SharedTestState.ConflictingGuidTypeName2, exception.TypeName2); @@ -79,7 +78,7 @@ public void PublicComVisibleTypeWithDuplicateGuidThrows() [Fact] public void DefaultProgIdIsTypeName() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleGuid); Assert.NotNull(comVisibleEntry); JObject entry = (JObject)comVisibleEntry.Value; @@ -89,7 +88,7 @@ public void DefaultProgIdIsTypeName() [Fact] public void ExplicitProgIdUsed() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ComVisibleCustomProgIdGuid); Assert.NotNull(comVisibleEntry); JObject entry = (JObject)comVisibleEntry.Value; @@ -99,19 +98,19 @@ public void ExplicitProgIdUsed() [Fact] public void ExplicitlyEmptyProgIdNotInClsidMap() { - JObject clsidMap = CreateClsidMap(sharedTestState.ComLibraryFixture); + JObject clsidMap = CreateClsidMap(sharedTestState.ComLibrary); JProperty comVisibleEntry = clsidMap.Property(SharedTestState.ExplicitNoProgIdGuid); Assert.NotNull(comVisibleEntry); JObject entry = (JObject)comVisibleEntry.Value; Assert.Null(entry.Property("progid")); } - private JObject CreateClsidMap(TestProjectFixture project) + private JObject CreateClsidMap(TestApp library) { using var testDirectory = TestDirectory.Create(); string clsidMapPath = Path.Combine(testDirectory.Path, "test.clsidmap"); - using (var assemblyStream = new FileStream(project.TestProject.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) + using (var assemblyStream = new FileStream(library.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) using (PEReader peReader = new PEReader(assemblyStream)) { if (peReader.HasMetadata) @@ -126,12 +125,10 @@ private JObject CreateClsidMap(TestProjectFixture project) { return JObject.Load(clsidMapReader); } - } public class SharedTestState : IDisposable { - public const string ComVisibleAssemblyName = "ComLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; public const string NotComVisibleGuid = "{6e30943e-b8ab-4e02-a904-9f1b5bb1c97d}"; public const string ComVisibleGuid = "{36e75747-aecd-43bf-9082-1a605889c762}"; public const string ComVisibleTypeName = "ComLibrary.ComVisible"; @@ -151,31 +148,20 @@ public class SharedTestState : IDisposable public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - - ComLibraryFixture = new TestProjectFixture("ComLibrary", RepoDirectories) - .EnsureRestored() - .BuildProject(); - - ComLibraryMissingGuidFixture = new TestProjectFixture("ComLibraryMissingGuid", RepoDirectories) - .EnsureRestored() - .BuildProject(); - - ComLibraryConflictingGuidFixture = new TestProjectFixture("ComLibraryConflictingGuid", RepoDirectories) - .EnsureRestored() - .BuildProject(); + ComLibrary = TestApp.CreateFromBuiltAssets("ComLibrary"); + ComLibraryMissingGuid = TestApp.CreateFromBuiltAssets("ComLibraryMissingGuid"); + ComLibraryConflictingGuid = TestApp.CreateFromBuiltAssets("ComLibraryConflictingGuid"); } - public RepoDirectoriesProvider RepoDirectories { get; } - public TestProjectFixture ComLibraryFixture { get; } - public TestProjectFixture ComLibraryMissingGuidFixture { get; } - public TestProjectFixture ComLibraryConflictingGuidFixture { get; } + public TestApp ComLibrary { get; } + public TestApp ComLibraryMissingGuid { get; } + public TestApp ComLibraryConflictingGuid { get; } public void Dispose() { - ComLibraryFixture.Dispose(); - ComLibraryMissingGuidFixture.Dispose(); - ComLibraryConflictingGuidFixture.Dispose(); + ComLibrary?.Dispose(); + ComLibraryMissingGuid?.Dispose(); + ComLibraryConflictingGuid?.Dispose(); } } } From 43f2d94ec6d06fecf13fa7ab8d6abe7faf73c34b Mon Sep 17 00:00:00 2001 From: kaybelev <156906788+kaybelev@users.noreply.github.com> Date: Fri, 19 Jan 2024 20:14:26 +0300 Subject: [PATCH 128/189] runtime: minor bugs fixed in coreclr and libraries (#97155) * Fixed error: . The error was detected using the Svace analyzer maded by ISP RAS. * Fixed error: . The error was detected using the Svace analyzer maded by ISP RAS. * Fixed error: . The error was detected using the Svace analyzer maded by ISP RAS. * Fixed error: . The error was detected using the Svace analyzer maded by ISP RAS. * Update filetime.cpp * Update process.cpp * Update MgmtConfigurationRecord.cs * Update src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/MgmtConfigurationRecord.cs Co-authored-by: Jan Kotas * Update src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/MgmtConfigurationRecord.cs Co-authored-by: Jan Kotas --------- Co-authored-by: Jan Kotas --- src/coreclr/pal/src/file/filetime.cpp | 8 +++++++- src/coreclr/pal/src/thread/process.cpp | 6 ++++++ .../aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs | 2 +- .../src/System/Configuration/MgmtConfigurationRecord.cs | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/coreclr/pal/src/file/filetime.cpp b/src/coreclr/pal/src/file/filetime.cpp index 2500d234d8b65a..d038c9f29eb595 100644 --- a/src/coreclr/pal/src/file/filetime.cpp +++ b/src/coreclr/pal/src/file/filetime.cpp @@ -263,7 +263,13 @@ BOOL PALAPI FileTimeToSystemTime( CONST FILETIME * lpFileTime, #else /* HAVE_GMTIME_R */ UnixSystemTime = gmtime( &UnixFileTime ); #endif /* HAVE_GMTIME_R */ - + if (!UnixSystemTime) + { + ERROR( "gmtime failed.\n" ); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + /* Convert unix system time to Windows system time. */ lpSystemTime->wDay = (WORD)UnixSystemTime->tm_mday; diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index f9f2703cf4b23d..b23eab001cca4e 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2821,6 +2821,12 @@ CorUnix::InitializeProcessCommandLine( if (lpwstrFullPath) { LPWSTR lpwstr = PAL_wcsrchr(lpwstrFullPath, '/'); + if (!lpwstr) + { + ERROR("Invalid full path\n"); + palError = ERROR_INTERNAL_ERROR; + goto exit; + } lpwstr[0] = '\0'; size_t n = PAL_wcslen(lpwstrFullPath) + 1; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs index b119f3bce5e4f7..dfc21b015428ea 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs @@ -414,7 +414,7 @@ private uint LookupIbcMethodToken(MetadataType methodMetadataType, uint ibcToken if (method.Name == methodName) { EcmaMethod ecmaCandidateMethod = method as EcmaMethod; - if (method == null) + if (ecmaCandidateMethod == null) continue; MetadataReader metadataReader = ecmaCandidateMethod.MetadataReader; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/MgmtConfigurationRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/MgmtConfigurationRecord.cs index 31e148711d8db6..4bcfe2a999fbbf 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/MgmtConfigurationRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/MgmtConfigurationRecord.cs @@ -335,6 +335,7 @@ internal ConfigurationSection FindImmediateParentSection(ConfigurationSection se string configKey = section.SectionInformation.SectionName; SectionRecord sectionRecord = GetSectionRecord(configKey, false); + Debug.Assert(sectionRecord != null); if (sectionRecord.HasLocationInputs) { SectionInput input = sectionRecord.LastLocationInput; From 5a6135e7e6e9fd19a1e319958cc3220dcad9a1e3 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Fri, 19 Jan 2024 18:21:31 +0100 Subject: [PATCH 129/189] [NativeAOT] Linux/ARM bring-up (3/n) (#97104) * Mask thumb bit when setting IP in a context (eg. palContext->SetIp((uintptr_t)&RhpThrowHwEx)) * Set ExInfo.m_pExContext in RhpThrowHwEx * Remove REGDISPLAY.GetAddrOfIP and it's only usage * Resolve ARM relocations (also workarounds LLD bug with thumb bit present both in addend and in symbol value) * Remove REGDISPLAY.pIP/SetAddrOfIP * Make most of the assembly code PIE compatible * NativeAOT: Enable DFEATURE_64BIT_ALIGNMENT on linux-arm * Enable NativeAOT linux-arm build * Enable DWARF exception handling for linux-arm * Fix UnwindFuncletInvokeThunk to skip over r2 register saved on stack by RhpCallFilterFunclet * Fix signature of P/Invoke native code in SafeHandleTest to match the managed one * Implement missing StackFrameIterator::InternalInit code for ARM * Fix thumb bit masking in InInterfaceDispatchHelper * Workaround: Ensure the Thumb bit is set when looking up method info in DWARF. We would fail to lookup methods at their first instruction otherwise. * Correctly convert the addend for IMAGE_REL_BASED_THUMB_MOV32[_PCREL] into the ELF relocations * Simplify CMake condition * Simplify NativeAotSupported conditions * Fix typo * Fix IMAGE_REL_BASED_THUMB_MOV32 conversion to ELF --- eng/Subsets.props | 2 +- src/coreclr/CMakeLists.txt | 2 +- src/coreclr/nativeaot/Runtime/CMakeLists.txt | 3 + src/coreclr/nativeaot/Runtime/EHHelpers.cpp | 9 +-- .../nativeaot/Runtime/StackFrameIterator.cpp | 42 +++++++------ .../nativeaot/Runtime/amd64/AsmOffsetsCpu.h | 12 ++-- .../nativeaot/Runtime/arm/AsmOffsetsCpu.h | 14 ++--- .../nativeaot/Runtime/arm/ExceptionHandling.S | 4 ++ src/coreclr/nativeaot/Runtime/arm/GcProbe.S | 6 +- .../Runtime/arm/InteropThunksHelpers.S | 2 +- src/coreclr/nativeaot/Runtime/arm/PInvoke.S | 3 +- .../nativeaot/Runtime/arm/StubDispatch.S | 2 +- .../Runtime/arm/UniversalTransition.S | 4 +- .../nativeaot/Runtime/arm/WriteBarriers.S | 42 +++++-------- .../nativeaot/Runtime/arm64/AsmOffsetsCpu.h | 14 ++--- src/coreclr/nativeaot/Runtime/regdisplay.h | 12 ---- .../Runtime/unix/UnixNativeCodeManager.cpp | 6 +- .../nativeaot/Runtime/unix/UnwindHelpers.cpp | 30 ++------- .../Runtime/unix/unixasmmacrosarm.inc | 11 +++- .../Runtime/windows/CoffNativeCodeManager.cpp | 4 -- .../Compiler/ObjectWriter/ElfObjectWriter.cs | 36 ++++++----- .../Compiler/ObjectWriter/ObjectWriter.cs | 61 +++++++++++-------- .../tools/aot/ILCompiler/ILCompiler.csproj | 1 + .../aot/crossgen2/crossgen2_publish.csproj | 1 + .../include/__libunwind_config.h | 4 ++ .../SmokeTests/PInvoke/PInvokeNative.cpp | 4 +- 26 files changed, 161 insertions(+), 170 deletions(-) diff --git a/eng/Subsets.props b/eng/Subsets.props index 0f78725c3af988..b769e1c18a0d60 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -112,7 +112,7 @@ - true + true true diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index ade1e03c92efd6..1c314d9bf624e0 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -147,7 +147,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) + if(CLR_CMAKE_HOST_ARCH_ARM64 OR CLR_CMAKE_HOST_ARCH_AMD64 OR CLR_CMAKE_HOST_ARCH_ARM) add_subdirectory(nativeaot) endif() endif(NOT CLR_CROSS_COMPONENTS_BUILD) diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index e6d89b44c0c40c..b6d871dce5c302 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -239,6 +239,9 @@ add_definitions(-DFEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) add_definitions(-DFEATURE_MANUALLY_MANAGED_CARD_BUNDLES) endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) +if(CLR_CMAKE_TARGET_ARCH_ARM) + add_definitions(-DFEATURE_64BIT_ALIGNMENT) +endif(CLR_CMAKE_TARGET_ARCH_ARM) add_definitions(-DFEATURE_CUSTOM_IMPORTS) add_definitions(-DFEATURE_DYNAMIC_CODE) diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index f383ff688dad95..957bab172f4f56 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -27,6 +27,7 @@ #include "rhbinder.h" #include "MethodTable.h" #include "MethodTable.inl" +#include "CommonMacros.inl" COOP_PINVOKE_HELPER(FC_BOOL_RET, RhpEHEnumInitFromStackFrameIterator, ( StackFrameIterator* pFrameIter, void ** pMethodStartAddressOut, EHEnum* pEHEnum)) @@ -327,7 +328,7 @@ static bool InWriteBarrierHelper(uintptr_t faultingIP) ASSERT(*(uint8_t*)writeBarrierAVLocations[i] != 0xE9); // jmp XXXXXXXX #endif - if (writeBarrierAVLocations[i] == faultingIP) + if (PCODEToPINSTR(writeBarrierAVLocations[i]) == faultingIP) return true; } #endif // USE_PORTABLE_HELPERS @@ -368,7 +369,7 @@ static bool InInterfaceDispatchHelper(uintptr_t faultingIP) ASSERT(*(uint8_t*)interfaceDispatchAVLocations[i] != 0xE9); // jmp XXXXXXXX #endif - if (interfaceDispatchAVLocations[i] == faultingIP) + if (PCODEToPINSTR(interfaceDispatchAVLocations[i]) == faultingIP) return true; } #endif // USE_PORTABLE_HELPERS @@ -458,7 +459,7 @@ int32_t __stdcall RhpHardwareExceptionHandler(uintptr_t faultCode, uintptr_t fau { *arg0Reg = faultCode; *arg1Reg = faultingIP; - palContext->SetIp((uintptr_t)&RhpThrowHwEx); + palContext->SetIp(PCODEToPINSTR((PCODE)&RhpThrowHwEx)); return EXCEPTION_CONTINUE_EXECUTION; } @@ -542,7 +543,7 @@ int32_t __stdcall RhpVectoredExceptionHandler(PEXCEPTION_POINTERS pExPtrs) if (translateToManagedException) { - pExPtrs->ContextRecord->SetIp((uintptr_t)&RhpThrowHwEx); + pExPtrs->ContextRecord->SetIp(PCODEToPINSTR((PCODE)&RhpThrowHwEx)); pExPtrs->ContextRecord->SetArg0Reg(faultCode); pExPtrs->ContextRecord->SetArg1Reg(faultingIP); diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 6e5b0fe1a0c13e..1943aa2bcb6d7a 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -179,8 +179,7 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF #if !defined(USE_PORTABLE_HELPERS) // @TODO: no portable version of regdisplay memset(&m_RegDisplay, 0, sizeof(m_RegDisplay)); m_RegDisplay.SetIP((PCODE)pFrame->m_RIP); - m_RegDisplay.SetAddrOfIP((PTR_PCODE)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP)); - SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + SetControlPC(dac_cast(m_RegDisplay.GetIP())); PTR_UIntNative pPreservedRegsCursor = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_PreservedRegs); @@ -388,7 +387,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO SetControlPC(dac_cast(pCtx->GetIp())); m_RegDisplay.SP = pCtx->GetSp(); m_RegDisplay.IP = pCtx->GetIp(); - m_RegDisplay.pIP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, IP); #ifdef TARGET_ARM // @@ -540,8 +538,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC #ifdef TARGET_ARM64 - m_RegDisplay.pIP = (PTR_PCODE)PTR_TO_REG(pCtx, Pc); - // // preserved regs // @@ -583,8 +579,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC #elif defined(TARGET_X86) || defined(TARGET_AMD64) - m_RegDisplay.pIP = (PTR_PCODE)PTR_TO_REG(pCtx, Rip); - // // preserved regs // @@ -611,6 +605,19 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC m_RegDisplay.pR10 = (PTR_UIntNative)PTR_TO_REG(pCtx, R10); m_RegDisplay.pR11 = (PTR_UIntNative)PTR_TO_REG(pCtx, R11); #endif // TARGET_AMD64 +#elif defined(TARGET_ARM) + + m_RegDisplay.pR0 = (PTR_UIntNative)PTR_TO_REG(pCtx, R0); + m_RegDisplay.pR1 = (PTR_UIntNative)PTR_TO_REG(pCtx, R1); + m_RegDisplay.pR4 = (PTR_UIntNative)PTR_TO_REG(pCtx, R4); + m_RegDisplay.pR5 = (PTR_UIntNative)PTR_TO_REG(pCtx, R5); + m_RegDisplay.pR6 = (PTR_UIntNative)PTR_TO_REG(pCtx, R6); + m_RegDisplay.pR7 = (PTR_UIntNative)PTR_TO_REG(pCtx, R7); + m_RegDisplay.pR8 = (PTR_UIntNative)PTR_TO_REG(pCtx, R8); + m_RegDisplay.pR9 = (PTR_UIntNative)PTR_TO_REG(pCtx, R9); + m_RegDisplay.pR10 = (PTR_UIntNative)PTR_TO_REG(pCtx, R10); + m_RegDisplay.pR11 = (PTR_UIntNative)PTR_TO_REG(pCtx, R11); + m_RegDisplay.pLR = (PTR_UIntNative)PTR_TO_REG(pCtx, Lr); #else PORTABILITY_ASSERT("StackFrameIterator::InternalInit"); #endif // TARGET_ARM @@ -779,10 +786,9 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() #ifdef TARGET_X86 // First, unwind RhpCallFunclet SP = (PTR_UIntNative)(m_RegDisplay.SP + 0x4); // skip the saved assembly-routine-EBP - m_RegDisplay.SetAddrOfIP(SP); m_RegDisplay.SetIP(*SP++); m_RegDisplay.SetSP((uintptr_t)dac_cast(SP)); - SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + SetControlPC(dac_cast(m_RegDisplay.GetIP())); ASSERT( EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) || @@ -908,12 +914,12 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() SP = (PTR_UIntNative)d; + // 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) ? 3 : 1; + 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) ? 3 : 1; - // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. m_funcletPtrs.pR4 = m_RegDisplay.pR4; m_funcletPtrs.pR5 = m_RegDisplay.pR5; @@ -966,7 +972,6 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_RegDisplay.pFP = SP++; - m_RegDisplay.SetAddrOfIP((PTR_PCODE)SP); m_RegDisplay.SetIP(*SP++); m_RegDisplay.pX19 = SP++; @@ -986,12 +991,11 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() #endif #if !defined(TARGET_ARM64) - m_RegDisplay.SetAddrOfIP((PTR_PCODE)SP); m_RegDisplay.SetIP(*SP++); #endif m_RegDisplay.SetSP((uintptr_t)dac_cast(SP)); - SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + SetControlPC(dac_cast(m_RegDisplay.GetIP())); // We expect to be called by the runtime's C# EH implementation, and since this function's notion of how // to unwind through the stub is brittle relative to the stub itself, we want to check as soon as we can. @@ -1170,10 +1174,9 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() stackFrame->UnwindNonVolatileRegisters(&m_RegDisplay); PTR_UIntNative addressOfPushedCallerIP = stackFrame->get_AddressOfPushedCallerIP(); - m_RegDisplay.SetAddrOfIP((PTR_PCODE)addressOfPushedCallerIP); m_RegDisplay.SetIP(*addressOfPushedCallerIP); m_RegDisplay.SetSP((uintptr_t)dac_cast(stackFrame->get_CallerSP())); - SetControlPC(dac_cast(*(m_RegDisplay.pIP))); + SetControlPC(dac_cast(m_RegDisplay.GetIP())); // All universal transition cases rely on conservative GC reporting being applied to the // full argument set that flowed into the call. Report the lower bound of this range (the @@ -1262,7 +1265,6 @@ void StackFrameIterator::UnwindThrowSiteThunk() ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif - m_RegDisplay.SetAddrOfIP(PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pContext, IP)); m_RegDisplay.SetIP(pContext->IP); m_RegDisplay.SetSP(pContext->GetSp()); SetControlPC(dac_cast(pContext->IP)); @@ -1355,7 +1357,7 @@ void StackFrameIterator::NextInternal() // if the thread is safe to walk, it better not have a hijack in place. ASSERT(!m_pThread->IsHijacked()); - SetControlPC(dac_cast(*(m_RegDisplay.GetAddrOfIP()))); + SetControlPC(dac_cast(m_RegDisplay.GetIP())); PTR_VOID collapsingTargetFrame = NULL; diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h index 8cc7f63f282de2..07a73c1e4bcb0f 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h @@ -71,7 +71,7 @@ PLAT_ASM_OFFSET(90, REGDISPLAY, Xmm) #else // !UNIX_AMD64_ABI -PLAT_ASM_SIZEOF(1b0, ExInfo) +PLAT_ASM_SIZEOF(1a8, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -79,7 +79,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(1a8, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(1a0, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) @@ -87,12 +87,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(188, StackFrameIterator) +PLAT_ASM_SIZEOF(180, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(178, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(180, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(170, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(178, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(50, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) @@ -108,7 +108,7 @@ PLAT_ASM_OFFSET(38, PAL_LIMITED_CONTEXT, R13) PLAT_ASM_OFFSET(40, PAL_LIMITED_CONTEXT, R14) PLAT_ASM_OFFSET(48, PAL_LIMITED_CONTEXT, R15) -PLAT_ASM_SIZEOF(90, REGDISPLAY) +PLAT_ASM_SIZEOF(88, REGDISPLAY) PLAT_ASM_OFFSET(78, REGDISPLAY, SP) PLAT_ASM_OFFSET(18, REGDISPLAY, pRbx) diff --git a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h index e76a1670de75ec..f5b6631f510b2e 100644 --- a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(140, ExInfo) +PLAT_ASM_SIZEOF(138, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) PLAT_ASM_OFFSET(8, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(0c, ExInfo, m_kind) PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(18, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(138, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(130, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(0c, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(14, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(120, StackFrameIterator) +PLAT_ASM_SIZEOF(118, StackFrameIterator) PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(114, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(118, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(10c, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(110, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(70, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(24, PAL_LIMITED_CONTEXT, IP) @@ -45,7 +45,7 @@ PLAT_ASM_OFFSET(20, PAL_LIMITED_CONTEXT, R11) PLAT_ASM_OFFSET(28, PAL_LIMITED_CONTEXT, SP) PLAT_ASM_OFFSET(2c, PAL_LIMITED_CONTEXT, LR) -PLAT_ASM_SIZEOF(88, REGDISPLAY) +PLAT_ASM_SIZEOF(80, REGDISPLAY) PLAT_ASM_OFFSET(38, REGDISPLAY, SP) PLAT_ASM_OFFSET(10, REGDISPLAY, pR4) @@ -56,4 +56,4 @@ PLAT_ASM_OFFSET(20, REGDISPLAY, pR8) PLAT_ASM_OFFSET(24, REGDISPLAY, pR9) PLAT_ASM_OFFSET(28, REGDISPLAY, pR10) PLAT_ASM_OFFSET(2c, REGDISPLAY, pR11) -PLAT_ASM_OFFSET(48, REGDISPLAY, D) +PLAT_ASM_OFFSET(40, REGDISPLAY, D) diff --git a/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S index 5ba38f17825014..7957793425554e 100644 --- a/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S @@ -62,6 +62,10 @@ NESTED_ENTRY RhpThrowHwEx, _TEXT, NoHandler str r3, [r1, #OFFSETOF__ExInfo__m_pPrevExInfo] // pExInfo->m_pPrevExInfo = m_pExInfoStackHead str r1, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] // m_pExInfoStackHead = pExInfo + // set the exception context field on the ExInfo + add r2, sp, #rsp_offsetof_Context // r2 <- PAL_LIMITED_CONTEXT* + str r2, [r1, #OFFSETOF__ExInfo__m_pExContext] // pExInfo->m_pExContext = pContext + mov r0, r4 // restore the exception code // r0 contains the exception code // r1 contains the address of the ExInfo diff --git a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S index 87ead1311f3c71..52fee443682882 100644 --- a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S @@ -1,14 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +.syntax unified +.thumb + #include #include "AsmOffsets.inc" .global RhpGcPoll2 LEAF_ENTRY RhpGcPoll - ldr r0, =RhpTrapThreads - ldr r0, [r0] + PREPARE_EXTERNAL_VAR_INDIRECT RhpTrapThreads, r0 cmp r0, #TrapThreadsFlags_None bne RhpGcPollRare bx lr diff --git a/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S b/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S index d8012f088a6ec1..eec5523d67500b 100644 --- a/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S +++ b/src/coreclr/nativeaot/Runtime/arm/InteropThunksHelpers.S @@ -42,7 +42,7 @@ NESTED_END RhCommonStub, _TEXT // IntPtr RhGetCommonStubAddress() // LEAF_ENTRY RhGetCommonStubAddress, _TEXT - ldr r0, =C_FUNC(RhCommonStub) + PREPARE_EXTERNAL_VAR RhCommonStub, r0 bx lr LEAF_END RhGetCommonStubAddress, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/PInvoke.S b/src/coreclr/nativeaot/Runtime/arm/PInvoke.S index eda3ccdc66195d..39136c17935ea8 100644 --- a/src/coreclr/nativeaot/Runtime/arm/PInvoke.S +++ b/src/coreclr/nativeaot/Runtime/arm/PInvoke.S @@ -49,8 +49,7 @@ LEAF_ENTRY RhpPInvokeReturn, _TEXT mov r2, #0 str r2, [r3, #OFFSETOF__Thread__m_pTransitionFrame] - ldr r3, =C_FUNC(RhpTrapThreads) - ldr r3, [r3] + PREPARE_EXTERNAL_VAR_INDIRECT RhpTrapThreads, r3 cbnz r3, LOCAL_LABEL(ReturnRareTrapThread) // TrapThreadsFlags_None = 0 bx lr diff --git a/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S b/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S index 331b6bd27d8e91..05cf4f919817d6 100644 --- a/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S +++ b/src/coreclr/nativeaot/Runtime/arm/StubDispatch.S @@ -125,7 +125,7 @@ LEAF_ENTRY RhpInterfaceDispatchSlow, _TEXT // for the universal thunk target is to be placed in sp-8 // and the universal thunk target address is to be placed in sp-4 str r12, [sp, #-8] - ldr r12, =C_FUNC(RhpCidResolve) + PREPARE_EXTERNAL_VAR RhpCidResolve, r12 str r12, [sp, #-4] // jump to universal transition thunk diff --git a/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S b/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S index 46a3929b9301a2..5db9aa8847d08d 100644 --- a/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S +++ b/src/coreclr/nativeaot/Runtime/arm/UniversalTransition.S @@ -102,7 +102,7 @@ NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler // frame, and the code at the call target is required to use only the transition frame // copies when dispatching this call to the eventual callee. - ldr r3, =C_FUNC(RhpFpTrashValues) + PREPARE_EXTERNAL_VAR RhpFpTrashValues, r3 vldr d0, [r3, #(0 * 8)] vldr d1, [r3, #(1 * 8)] vldr d2, [r3, #(2 * 8)] @@ -112,7 +112,7 @@ NESTED_ENTRY Rhp\FunctionName, _TEXT, NoHandler vldr d6, [r3, #(6 * 8)] vldr d7, [r3, #(7 * 8)] - ldr r3, =C_FUNC(RhpIntegerTrashValues) + PREPARE_EXTERNAL_VAR RhpIntegerTrashValues, r3 ldr r2, [r3, #(2 * 4)] ldr r3, [r3, #(3 * 4)] diff --git a/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S b/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S index 30e20c4bea401f..2d22c9bf1f18b9 100644 --- a/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S +++ b/src/coreclr/nativeaot/Runtime/arm/WriteBarriers.S @@ -12,8 +12,7 @@ .macro UPDATE_GC_SHADOW BASENAME, REFREG, DESTREG // If g_GCShadow is 0, don't perform the check. - ldr r12, =C_FUNC(g_GCShadow) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_GCShadow, r12 cbz r12, LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG) // Save DESTREG since we're about to modify it (and we need the original value both within the macro and @@ -27,16 +26,13 @@ push \DESTREG // Transform DESTREG into the equivalent address in the shadow heap. - ldr r12, =C_FUNC(g_lowest_address) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_lowest_address, r12 sub \DESTREG, r12 cmp \DESTREG, #0 blo LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) - ldr r12, =C_FUNC(g_GCShadow) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_GCShadow, r12 add \DESTREG, r12 - ldr r12, =C_FUNC(g_GCShadowEnd) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_GCShadowEnd, r12 cmp \DESTREG, r12 bhs LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_PopThenDone_\REFREG) @@ -105,13 +101,11 @@ LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG): // If the reference is to an object that's not in an ephemeral generation we have no need to track it // (since the object won't be collected or moved by an ephemeral collection). - ldr r12, =C_FUNC(g_ephemeral_low) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_ephemeral_low, r12 cmp \REFREG, r12 blo LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) - ldr r12, =C_FUNC(g_ephemeral_high) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_ephemeral_high, r12 cmp \REFREG, r12 bhs LOCAL_LABEL(\BASENAME\()_EXIT_\REFREG) @@ -119,8 +113,7 @@ LOCAL_LABEL(\BASENAME\()_UpdateShadowHeap_Done_\REFREG): // track this write. The location address is translated into an offset in the card table bitmap. We set // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write // the byte if it hasn't already been done since writes are expensive and impact scaling. - ldr r12, =C_FUNC(g_card_table) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_card_table, r12 add r0, r12, r0, lsr #LOG2_CLUMP_SIZE ldrb r12, [r0] cmp r12, #0x0FF @@ -198,12 +191,10 @@ DEFINE_UNCHECKED_WRITE_BARRIER r1, r1 // The location being updated might not even lie in the GC heap (a handle or stack location for instance), // in which case no write barrier is required. - ldr r12, =C_FUNC(g_lowest_address) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_lowest_address, r12 cmp r0, r12 blo LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) - ldr r12, =C_FUNC(g_highest_address) - ldr r12, [r12] + PREPARE_EXTERNAL_VAR_INDIRECT g_highest_address, r12 cmp r0, r12 bhs LOCAL_LABEL(\BASENAME\()_NoBarrierRequired_\REFREG) @@ -335,12 +326,10 @@ ALTERNATE_ENTRY RhpByRefAssignRefAVLocation2 str r2, [r0] // Check whether the writes were even into the heap. If not there's no card update required. - ldr r3, =C_FUNC(g_lowest_address) - ldr r3, [r3] + PREPARE_EXTERNAL_VAR_INDIRECT g_lowest_address, r3 cmp r0, r3 blo LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) - ldr r3, =C_FUNC(g_highest_address) - ldr r3, [r3] + PREPARE_EXTERNAL_VAR_INDIRECT g_highest_address, r3 cmp r0, r3 bhs LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) @@ -350,12 +339,10 @@ ALTERNATE_ENTRY RhpByRefAssignRefAVLocation2 // If the reference is to an object that's not in an ephemeral generation we have no need to track it // (since the object won't be collected or moved by an ephemeral collection). - ldr r3, =C_FUNC(g_ephemeral_low) - ldr r3, [r3] + PREPARE_EXTERNAL_VAR_INDIRECT g_ephemeral_low, r3 cmp r2, r3 blo LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) - ldr r3, =C_FUNC(g_ephemeral_high) - ldr r3, [r3] + PREPARE_EXTERNAL_VAR_INDIRECT g_ephemeral_high, r3 cmp r2, r3 bhs LOCAL_LABEL(RhpByRefAssignRef_NotInHeap) @@ -368,8 +355,7 @@ ALTERNATE_ENTRY RhpByRefAssignRefAVLocation2 // track this write. The location address is translated into an offset in the card table bitmap. We set // an entire byte in the card table since it's quicker than messing around with bitmasks and we only write // the byte if it hasn't already been done since writes are expensive and impact scaling. - ldr r3, =C_FUNC(g_card_table) - ldr r3, [r3] + PREPARE_EXTERNAL_VAR_INDIRECT g_card_table, r3 add r2, r3, r2, lsr #LOG2_CLUMP_SIZE ldrb r3, [r2] cmp r3, #0x0FF diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h index cfef3d7f05f08f..529ab2c47331fe 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(298, ExInfo) +PLAT_ASM_SIZEOF(290, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(290, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(288, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(270, StackFrameIterator) +PLAT_ASM_SIZEOF(268, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(260, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(268, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(258, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(260, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(C0, PAL_LIMITED_CONTEXT) @@ -49,7 +49,7 @@ PLAT_ASM_OFFSET(68, PAL_LIMITED_CONTEXT, X28) PLAT_ASM_OFFSET(70, PAL_LIMITED_CONTEXT, SP) PLAT_ASM_OFFSET(78, PAL_LIMITED_CONTEXT, IP) -PLAT_ASM_SIZEOF(150, REGDISPLAY) +PLAT_ASM_SIZEOF(148, REGDISPLAY) PLAT_ASM_OFFSET(f8, REGDISPLAY, SP) PLAT_ASM_OFFSET(98, REGDISPLAY, pX19) @@ -64,4 +64,4 @@ PLAT_ASM_OFFSET(d8, REGDISPLAY, pX27) PLAT_ASM_OFFSET(e0, REGDISPLAY, pX28) PLAT_ASM_OFFSET(e8, REGDISPLAY, pFP) PLAT_ASM_OFFSET(f0, REGDISPLAY, pLR) -PLAT_ASM_OFFSET(110, REGDISPLAY, D) +PLAT_ASM_OFFSET(108, REGDISPLAY, D) diff --git a/src/coreclr/nativeaot/Runtime/regdisplay.h b/src/coreclr/nativeaot/Runtime/regdisplay.h index eae75eabe2e6c4..0246d0039477a3 100644 --- a/src/coreclr/nativeaot/Runtime/regdisplay.h +++ b/src/coreclr/nativeaot/Runtime/regdisplay.h @@ -30,7 +30,6 @@ struct REGDISPLAY #endif // TARGET_AMD64 uintptr_t SP; - PTR_PCODE pIP; PCODE IP; #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI) @@ -41,13 +40,11 @@ struct REGDISPLAY #endif // TARGET_AMD64 && !UNIX_AMD64_ABI inline PCODE GetIP() { return IP; } - inline PTR_PCODE GetAddrOfIP() { return pIP; } inline uintptr_t GetSP() { return SP; } inline uintptr_t GetFP() { return *pRbp; } inline uintptr_t GetPP() { return *pRbx; } inline void SetIP(PCODE IP) { this->IP = IP; } - inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } inline void SetSP(uintptr_t SP) { this->SP = SP; } }; @@ -71,7 +68,6 @@ struct REGDISPLAY PTR_UIntNative pLR; uintptr_t SP; - PTR_PCODE pIP; PCODE IP; uint64_t D[16-8]; // preserved D registers D8..D15 (note that D16-D31 are not preserved according to the ABI spec) @@ -80,11 +76,9 @@ struct REGDISPLAY // their values, not their addresses inline PCODE GetIP() { return IP; } - inline PTR_PCODE GetAddrOfIP() { return pIP; } inline uintptr_t GetSP() { return SP; } inline uintptr_t GetFP() { return *pR11; } inline void SetIP(PCODE IP) { this->IP = IP; } - inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } inline void SetSP(uintptr_t SP) { this->SP = SP; } }; @@ -125,7 +119,6 @@ struct REGDISPLAY PTR_UIntNative pLR; // X30 uintptr_t SP; - PTR_PCODE pIP; PCODE IP; uint64_t D[16-8]; // Only the bottom 64-bit value of the V registers V8..V15 needs to be preserved @@ -135,12 +128,10 @@ struct REGDISPLAY // their values, not their addresses inline PCODE GetIP() { return IP; } - inline PTR_PCODE GetAddrOfIP() { return pIP; } inline uintptr_t GetSP() { return SP; } inline uintptr_t GetFP() { return *pFP; } inline void SetIP(PCODE IP) { this->IP = IP; } - inline void SetAddrOfIP(PTR_PCODE pIP) { this->pIP = pIP; } inline void SetSP(uintptr_t SP) { this->SP = SP; } }; #elif defined(TARGET_WASM) @@ -150,16 +141,13 @@ struct REGDISPLAY // TODO: WebAssembly doesn't really have registers. What exactly do we need here? uintptr_t SP; - PTR_PCODE pIP; PCODE IP; inline PCODE GetIP() { return NULL; } - inline PTR_PCODE GetAddrOfIP() { return NULL; } inline uintptr_t GetSP() { return 0; } inline uintptr_t GetFP() { return 0; } inline void SetIP(PCODE IP) { } - inline void SetAddrOfIP(PTR_PCODE pIP) { } inline void SetSP(uintptr_t SP) { } }; #endif // HOST_X86 || HOST_AMD64 || HOST_ARM || HOST_ARM64 || HOST_WASM diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 9407499e329ca1..2a75e83bbfdfb8 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -87,12 +87,12 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, unw_proc_info_t procInfo; - if (!UnwindHelpers::GetUnwindProcInfo((PCODE)ControlPC, m_UnwindInfoSections, &procInfo)) + if (!UnwindHelpers::GetUnwindProcInfo(PINSTRToPCODE((TADDR)ControlPC), m_UnwindInfoSections, &procInfo)) { return false; } - assert((procInfo.start_ip <= (PCODE)ControlPC) && ((PCODE)ControlPC < procInfo.end_ip)); + assert((procInfo.start_ip <= PINSTRToPCODE((TADDR)ControlPC)) && (PINSTRToPCODE((TADDR)ControlPC) < procInfo.end_ip)); pMethodInfo->start_ip = procInfo.start_ip; pMethodInfo->format = procInfo.format; @@ -607,7 +607,7 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(MethodInfo * pMetho { unw_proc_info_t procInfo; - bool result = UnwindHelpers::GetUnwindProcInfo((PCODE)pvAddress, m_UnwindInfoSections, &procInfo); + bool result = UnwindHelpers::GetUnwindProcInfo(PINSTRToPCODE((TADDR)pvAddress), m_UnwindInfoSections, &procInfo); ASSERT(result); if (branchTarget < procInfo.start_ip || branchTarget >= procInfo.end_ip) diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 6fd0df0c63546f..91ccc1e631d96f 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -109,7 +109,6 @@ struct Registers_REGDISPLAY : REGDISPLAY case UNW_REG_IP: case UNW_X86_64_RIP: IP = value; - pIP = (PTR_PCODE)location; return; case UNW_REG_SP: SP = value; @@ -198,11 +197,7 @@ struct Registers_REGDISPLAY : REGDISPLAY uint64_t getIP() const { return IP; } - void setIP(uint64_t value, uint64_t location) - { - IP = value; - pIP = (PTR_PCODE)location; - } + void setIP(uint64_t value, uint64_t location) { IP = value; } uint64_t getRBP() const { return *pRbp; } void setRBP(uint64_t value, uint64_t location) { pRbp = (PTR_UIntNative)location; } @@ -260,7 +255,6 @@ struct Registers_REGDISPLAY : REGDISPLAY { case UNW_REG_IP: IP = value; - pIP = (PTR_PCODE)location; return; case UNW_REG_SP: SP = value; @@ -324,11 +318,7 @@ struct Registers_REGDISPLAY : REGDISPLAY uint64_t getIP() const { return IP; } - void setIP(uint64_t value, uint64_t location) - { - IP = value; - pIP = (PTR_PCODE)location; - } + void setIP(uint64_t value, uint64_t location) { IP = value; } uint64_t getEBP() const { return *pRbp; } void setEBP(uint64_t value, uint64_t location) { pRbp = (PTR_UIntNative)location; } @@ -360,8 +350,7 @@ struct Registers_REGDISPLAY : REGDISPLAY uint32_t getSP() const { return SP;} void setSP(uint32_t value, uint32_t location) { SP = value;} uint32_t getIP() const { return IP;} - void setIP(uint32_t value, uint32_t location) - { IP = value; pIP = (PTR_UIntNative)location; } + void setIP(uint32_t value, uint32_t location) { IP = value; } uint32_t getFP() const { return *pR11;} void setFP(uint32_t value, uint32_t location) { pR11 = (PTR_UIntNative)location;} }; @@ -439,12 +428,6 @@ void Registers_REGDISPLAY::setRegister(int num, uint32_t value, uint32_t locatio if (num == UNW_REG_IP || num == UNW_ARM_IP) { IP = value; - /* the location could be NULL, we could try to recover - pointer to value in stack from pLR */ - if ((!location) && pLR && (*pLR == value)) - pIP = pLR; - else - pIP = (PTR_UIntNative)location; return; } @@ -520,8 +503,7 @@ struct Registers_REGDISPLAY : REGDISPLAY 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; pIP = (PTR_UIntNative)location; } + 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_UIntNative)location;} }; @@ -815,10 +797,6 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t fo return false; } -#if !defined(TARGET_ARM64) - regs->pIP = PTR_PCODE(regs->SP - sizeof(TADDR)); -#endif - #elif defined(_LIBUNWIND_ARM_EHABI) PORTABILITY_ASSERT("StepFrame"); #else diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc index 9f1d17a46188ae..7961fdb1dbcf40 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc @@ -68,7 +68,16 @@ C_FUNC(\Name): .endm .macro PREPARE_EXTERNAL_VAR Name, HelperReg - ldr \HelperReg, [pc, #C_FUNC(\Name)@GOTPCREL] + movw \HelperReg, #:lower16:C_FUNC(\Name) - (. + 12) + movt \HelperReg, #:upper16:C_FUNC(\Name) - (. + 12) + add \HelperReg, pc +.endm + +.macro PREPARE_EXTERNAL_VAR_INDIRECT Name, HelperReg + movw \HelperReg, #:lower16:C_FUNC(\Name) - (. + 12) + movt \HelperReg, #:upper16:C_FUNC(\Name) - (. + 12) + add \HelperReg, pc + ldr \HelperReg, [\HelperReg] .endm .macro push_nonvol_reg Register diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index cc409cf2167a16..cbdec015f117c6 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -636,8 +636,6 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, pRegisterSet->SP = context.Rsp; pRegisterSet->IP = context.Rip; - pRegisterSet->pIP = PTR_PCODE(pRegisterSet->SP - sizeof(TADDR)); - if (!(flags & USFF_GcUnwind)) { memcpy(pRegisterSet->Xmm, &context.Xmm6, sizeof(pRegisterSet->Xmm)); @@ -667,8 +665,6 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, pRegisterSet->SP = context.Sp; pRegisterSet->IP = context.Pc; - pRegisterSet->pIP = contextPointers.Lr; - if (!(flags & USFF_GcUnwind)) { for (int i = 8; i < 16; i++) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs index 17fa66b7c7a3f6..aa4b39552b6930 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -169,18 +169,13 @@ protected internal override unsafe void EmitRelocation( { fixed (byte *pData = data) { - if (relocType is IMAGE_REL_BASED_REL32 && _machine is EM_386 or EM_X86_64) + addend -= relocType switch { - addend -= 4; - } - else if (relocType is IMAGE_REL_BASED_THUMB_BRANCH24) - { - addend -= 4; - } - else if (relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL) - { - addend -= 12; - } + IMAGE_REL_BASED_REL32 => 4, + IMAGE_REL_BASED_THUMB_BRANCH24 => 4, + IMAGE_REL_BASED_THUMB_MOV32_PCREL => 12, + _ => 0 + }; if (!_useInlineRelocationAddends) { @@ -196,12 +191,24 @@ protected internal override unsafe void EmitRelocation( } else { - if (addend != 0) + if (relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL or IMAGE_REL_BASED_THUMB_MOV32) + { + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + addend += inlineValue; + Debug.Assert(addend >= short.MinValue && addend <= short.MaxValue); + // This expands into two relocations where each of them has to have the + // same base 16-bit addend + PC difference between the two instructions. + addend = relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL ? + (long)((((uint)(addend + 4) & 0xffff) << 16) + (ushort)addend) : + (long)((((uint)(addend) & 0xffff) << 16) + (ushort)addend); + Relocation.WriteValue(relocType, (void*)pData, addend); + } + else if (addend != 0) { long inlineValue = Relocation.ReadValue(relocType, (void*)pData); Relocation.WriteValue(relocType, (void*)pData, inlineValue + addend); - addend = 0; } + addend = 0; } } @@ -219,11 +226,10 @@ private protected override void EmitSymbolTable( var type = (section.SectionHeader.Flags & SHF_TLS) == SHF_TLS ? STT_TLS : definition.Size > 0 ? STT_FUNC : STT_NOTYPE; - ulong thumbBit = _machine == EM_ARM && type == STT_FUNC ? 1u : 0u; sortedSymbols.Add(new ElfSymbol { Name = name, - Value = (ulong)definition.Value | thumbBit, + Value = (ulong)definition.Value, Size = (ulong)definition.Size, Section = _sections[definition.SectionIndex], Info = (byte)(type | (STB_GLOBAL << 4)), diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs index fde0bb8a53d01a..042cf91bc238c2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs @@ -12,6 +12,7 @@ using Internal.TypeSystem; using Internal.TypeSystem.TypesDebugInfo; using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; +using static ILCompiler.DependencyAnalysis.RelocType; namespace ILCompiler.ObjectWriter { @@ -144,7 +145,7 @@ private protected static ObjectNodeSection GetSharedSection(ObjectNodeSection se return new ObjectNodeSection(standardSectionPrefix + section.Name, section.Type, key); } - private void EmitOrResolveRelocation( + private unsafe void EmitOrResolveRelocation( int sectionIndex, long offset, Span data, @@ -153,32 +154,41 @@ private void EmitOrResolveRelocation( long addend) { if (!_usesSubsectionsViaSymbols && - relocType is RelocType.IMAGE_REL_BASED_REL32 or RelocType.IMAGE_REL_BASED_RELPTR32 or RelocType.IMAGE_REL_BASED_ARM64_BRANCH26 && + relocType is IMAGE_REL_BASED_REL32 or IMAGE_REL_BASED_RELPTR32 or IMAGE_REL_BASED_ARM64_BRANCH26 + or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL && _definedSymbols.TryGetValue(symbolName, out SymbolDefinition definedSymbol) && definedSymbol.SectionIndex == sectionIndex) { // Resolve the relocation to already defined symbol and write it into data - switch (relocType) + fixed (byte *pData = data) { - case RelocType.IMAGE_REL_BASED_REL32: - addend += BinaryPrimitives.ReadInt32LittleEndian(data); - addend -= 4; - BinaryPrimitives.WriteInt32LittleEndian(data, (int)(definedSymbol.Value - offset) + (int)addend); - return; - - case RelocType.IMAGE_REL_BASED_RELPTR32: - addend += BinaryPrimitives.ReadInt32LittleEndian(data); - BinaryPrimitives.WriteInt32LittleEndian(data, (int)(definedSymbol.Value - offset) + (int)addend); - return; - - case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: - var ins = BinaryPrimitives.ReadUInt32LittleEndian(data) & 0xFC000000; - BinaryPrimitives.WriteUInt32LittleEndian(data, (((uint)(int)(definedSymbol.Value - offset) >> 2) & 0x3FFFFFF) | ins); - return; + // RyuJIT generates the Thumb bit in the addend and we also get it from + // the symbol value. The AAELF ABI specification defines the R_ARM_THM_JUMP24 + // and R_ARM_THM_MOVW_PREL_NC relocations using the formula ((S + A) | T) – P. + // The thumb bit is thus supposed to be only added once. + // For R_ARM_THM_JUMP24 the thumb bit cannot be encoded, so mask it out. + long maskThumbBitOut = relocType is IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL ? 1 : 0; + long maskThumbBitIn = relocType is IMAGE_REL_BASED_THUMB_MOV32_PCREL ? 1 : 0; + + addend -= relocType switch + { + IMAGE_REL_BASED_REL32 => 4, + IMAGE_REL_BASED_THUMB_BRANCH24 => 4, + IMAGE_REL_BASED_THUMB_MOV32_PCREL => 12, + _ => 0 + }; + + addend += definedSymbol.Value & ~maskThumbBitOut; + addend += Relocation.ReadValue(relocType, (void*)pData); + addend |= definedSymbol.Value & maskThumbBitIn; + addend -= offset; + Relocation.WriteValue(relocType, (void*)pData, addend); } } - - EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); + else + { + EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); + } } /// @@ -386,19 +396,20 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection false false + false false true diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj b/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj index 2416920ed40552..b1c2a1835bfaab 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj +++ b/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj @@ -10,6 +10,7 @@ false false + false false true diff --git a/src/native/external/llvm-libunwind/include/__libunwind_config.h b/src/native/external/llvm-libunwind/include/__libunwind_config.h index b925489e3e7fe6..6fdc48ed30a292 100644 --- a/src/native/external/llvm-libunwind/include/__libunwind_config.h +++ b/src/native/external/llvm-libunwind/include/__libunwind_config.h @@ -14,6 +14,10 @@ #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ !defined(__ARM_DWARF_EH__) && !defined(__SEH__) #define _LIBUNWIND_ARM_EHABI +// Until ObjWriter is modified to convert DWARF to EHABI we want to +// support both. +#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 +#define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 #endif #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 8 diff --git a/src/tests/nativeaot/SmokeTests/PInvoke/PInvokeNative.cpp b/src/tests/nativeaot/SmokeTests/PInvoke/PInvokeNative.cpp index bd6bb32e0e1bbe..6212c9b1b988d6 100644 --- a/src/tests/nativeaot/SmokeTests/PInvoke/PInvokeNative.cpp +++ b/src/tests/nativeaot/SmokeTests/PInvoke/PInvokeNative.cpp @@ -318,9 +318,9 @@ DLL_EXPORT bool __stdcall ReleaseMemory(void *mem) return true; } -DLL_EXPORT bool __stdcall SafeHandleTest(HANDLE sh, long shValue) +DLL_EXPORT bool __stdcall SafeHandleTest(HANDLE sh, int64_t shValue) { - return (long)((size_t)(sh)) == shValue; + return (int64_t)((size_t)(sh)) == shValue; } DLL_EXPORT long __stdcall SafeHandleOutTest(HANDLE **sh) From 2d751cac7311b344c237df3b3c63b33434b52217 Mon Sep 17 00:00:00 2001 From: Ruihan-Yin <107431934+Ruihan-Yin@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:39:55 -0800 Subject: [PATCH 130/189] Enable EVEX feature: Embedded Rounding for Avx512F.Add() (#94684) * some workaround with embedded rounding in compiler backend. * extend _idEvexbContext to 2bit to distinguish embedded broadcast and embedded rounding * Expose APIs with rounding mode. * Apply format patch * Do not include the third parameter in Avx512.Add(left, right) * split _idEvexbContext bits and made a explicit convert function from uint8_t to insOpts for embedded rounding mode. * Remove unexpected comment-out * Fix unexpected deletion * resolve comments: removed redundent bits in instDesc for EVEX.b context. Introduced `emitDispEmbRounding` to display the embedded rounding feature in the disassembly. * bug fix: fix un-needed assertion check. * Apply format patch. * Resolve comments: merge INS_OPTS_EVEX_b and INS_OPTS_EVEX_er_rd Do a pre-check for embedded rounding before lowering. * Add a helper function to generalize the logic when lowering the embedded rounding intrinsics. * Resolve comments: 1. fix typo in commnets 2. Add SetEvexBroadcastIfNeeded 3. Added mask in insOpts * 1. Add unit case for non-default rounding mode 2. removed round-to-even, the default option from InsOpts as it will be handled on the default path. * formatting * 1. Create a fallback jump table for embedded rounding APIs when control byte is not constant. 2. Create a template to generate the unit tests for embedded rounding APIs. 3. nit: fix naming. * remove hand-written unit tests for embedded rounding. * formatting * Resolve comments. * formatting * revert changes: let SetEmbRoundingMode accept unexpected values to accomadate the jump table generatation logics. --- src/coreclr/jit/codegen.h | 1 + src/coreclr/jit/emit.h | 53 ++- src/coreclr/jit/emitxarch.cpp | 128 +++++-- src/coreclr/jit/emitxarch.h | 37 +- src/coreclr/jit/gentree.h | 44 +++ src/coreclr/jit/hwintrinsic.h | 26 +- src/coreclr/jit/hwintrinsiccodegenxarch.cpp | 59 ++++ src/coreclr/jit/hwintrinsiclistxarch.h | 2 +- src/coreclr/jit/hwintrinsicxarch.cpp | 10 + src/coreclr/jit/instr.cpp | 4 +- src/coreclr/jit/instr.h | 10 +- src/coreclr/jit/lowerxarch.cpp | 62 ++++ src/coreclr/jit/lsraxarch.cpp | 7 + .../X86/Avx512F.PlatformNotSupported.cs | 5 + .../System/Runtime/Intrinsics/X86/Avx512F.cs | 5 + .../System/Runtime/Intrinsics/X86/Enums.cs | 20 ++ .../ref/System.Runtime.Intrinsics.cs | 8 + .../GenerateHWIntrinsicTests_X86.cs | 9 + .../Shared/SimpleBinOpEmbRounding.template | 315 ++++++++++++++++++ 19 files changed, 760 insertions(+), 45 deletions(-) create mode 100644 src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 22f4e58b094a2c..e1af485a0eb4f6 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -971,6 +971,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genHWIntrinsic_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber reg, GenTree* rmOp); void genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival); void genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr); + void genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival); void genHWIntrinsic_R_R_RM( GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, GenTree* op2); void genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival); diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 51ed6b72a0c520..08d91386d8bb22 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -774,8 +774,12 @@ class emitter unsigned _idCallAddr : 1; // IL indirect calls: can make a direct call to iiaAddr unsigned _idNoGC : 1; // Some helpers don't get recorded in GC tables #if defined(TARGET_XARCH) - unsigned _idEvexbContext : 1; // does EVEX.b need to be set. -#endif // TARGET_XARCH + // EVEX.b can indicate several context: embedded broadcast, embedded rounding. + // For normal and embedded broadcast intrinsics, EVEX.L'L has the same semantic, vector length. + // For embedded rounding, EVEX.L'L semantic changes to indicate the rounding mode. + // Multiple bits in _idEvexbContext are used to inform emitter to specially handle the EVEX.L'L bits. + unsigned _idEvexbContext : 2; +#endif // TARGET_XARCH #ifdef TARGET_ARM64 @@ -808,8 +812,8 @@ class emitter //////////////////////////////////////////////////////////////////////// // Space taken up to here: - // x86: 47 bits - // amd64: 47 bits + // x86: 48 bits + // amd64: 48 bits // arm: 48 bits // arm64: 53 bits // loongarch64: 46 bits @@ -828,7 +832,7 @@ class emitter #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #define ID_EXTRA_BITFIELD_BITS (14) #elif defined(TARGET_XARCH) -#define ID_EXTRA_BITFIELD_BITS (15) +#define ID_EXTRA_BITFIELD_BITS (16) #else #error Unsupported or unset target architecture #endif @@ -863,8 +867,8 @@ class emitter //////////////////////////////////////////////////////////////////////// // Space taken up to here (with/without prev offset, assuming host==target): - // x86: 53/49 bits - // amd64: 54/49 bits + // x86: 54/50 bits + // amd64: 55/50 bits // arm: 54/50 bits // arm64: 60/55 bits // loongarch64: 53/48 bits @@ -880,8 +884,8 @@ class emitter //////////////////////////////////////////////////////////////////////// // Small constant size (with/without prev offset, assuming host==target): - // x86: 11/15 bits - // amd64: 10/15 bits + // x86: 10/14 bits + // amd64: 9/14 bits // arm: 10/14 bits // arm64: 4/9 bits // loongarch64: 11/16 bits @@ -1578,15 +1582,35 @@ class emitter } #ifdef TARGET_XARCH - bool idIsEvexbContext() const + bool idIsEvexbContextSet() const { return _idEvexbContext != 0; } - void idSetEvexbContext() + + void idSetEvexbContext(insOpts instOptions) { assert(_idEvexbContext == 0); - _idEvexbContext = 1; - assert(_idEvexbContext == 1); + if (instOptions == INS_OPTS_EVEX_eb_er_rd) + { + _idEvexbContext = 1; + } + else if (instOptions == INS_OPTS_EVEX_er_ru) + { + _idEvexbContext = 2; + } + else if (instOptions == INS_OPTS_EVEX_er_rz) + { + _idEvexbContext = 3; + } + else + { + unreached(); + } + } + + unsigned idGetEvexbContext() const + { + return _idEvexbContext; } #endif @@ -2166,6 +2190,7 @@ class emitter void emitDispInsOffs(unsigned offs, bool doffs); void emitDispInsHex(instrDesc* id, BYTE* code, size_t sz); void emitDispEmbBroadcastCount(instrDesc* id); + void emitDispEmbRounding(instrDesc* id); void emitDispIns(instrDesc* id, bool isNew, bool doffs, @@ -3814,7 +3839,7 @@ inline unsigned emitter::emitGetInsCIargs(instrDesc* id) // emitAttr emitter::emitGetMemOpSize(instrDesc* id) const { - if (id->idIsEvexbContext()) + if (id->idIsEvexbContextSet()) { // should have the assumption that Evex.b now stands for the embedded broadcast context. // reference: Section 2.7.5 in Intel 64 and ia-32 architectures software developer's manual volume 2. diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 9bcb574578cc6e..c281b2d08c3cb3 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -1139,6 +1139,30 @@ static bool isLowSimdReg(regNumber reg) #endif } +//------------------------------------------------------------------------ +// GetEmbRoundingMode: Get the rounding mode for embedded rounding +// +// Arguments: +// mode -- the flag from the corresponding GenTree node indicating the mode. +// +// Return Value: +// the instruction option carrying the rounding mode information. +// +insOpts emitter::GetEmbRoundingMode(uint8_t mode) const +{ + switch (mode) + { + case 1: + return INS_OPTS_EVEX_eb_er_rd; + case 2: + return INS_OPTS_EVEX_er_ru; + case 3: + return INS_OPTS_EVEX_er_rz; + default: + unreached(); + } +} + //------------------------------------------------------------------------ // encodeRegAsIval: Encodes a register as an ival for use by a SIMD instruction // @@ -1309,18 +1333,50 @@ emitter::code_t emitter::AddEvexPrefix(const instrDesc* id, code_t code, emitAtt if (attr == EA_32BYTE) { - // Set L bit to 1 in case of instructions that operate on 256-bits. + // Set EVEX.L'L bits to 01 in case of instructions that operate on 256-bits. code |= LBIT_IN_BYTE_EVEX_PREFIX; } else if (attr == EA_64BYTE) { - // Set L' bits to 11 in case of instructions that operate on 512-bits. + // Set EVEX.L'L bits to 10 in case of instructions that operate on 512-bits. code |= LPRIMEBIT_IN_BYTE_EVEX_PREFIX; } - if (id->idIsEvexbContext()) + if (id->idIsEvexbContextSet()) { code |= EVEX_B_BIT; + + if (!id->idHasMem()) + { + // embedded rounding case. + unsigned roundingMode = id->idGetEvexbContext(); + if (roundingMode == 1) + { + // {rd-sae} + code &= ~(LPRIMEBIT_IN_BYTE_EVEX_PREFIX); + code |= LBIT_IN_BYTE_EVEX_PREFIX; + } + else if (roundingMode == 2) + { + // {ru-sae} + code |= LPRIMEBIT_IN_BYTE_EVEX_PREFIX; + code &= ~(LBIT_IN_BYTE_EVEX_PREFIX); + } + else if (roundingMode == 3) + { + // {rz-sae} + code |= LPRIMEBIT_IN_BYTE_EVEX_PREFIX; + code |= LBIT_IN_BYTE_EVEX_PREFIX; + } + else + { + unreached(); + } + } + else + { + assert(id->idGetEvexbContext() == 1); + } } regNumber maskReg = REG_NA; @@ -6742,11 +6798,7 @@ void emitter::emitIns_R_R_A( id->idIns(ins); id->idReg1(reg1); id->idReg2(reg2); - if (instOptions == INS_OPTS_EVEX_b) - { - assert(UseEvexEncoding()); - id->idSetEvexbContext(); - } + SetEvexBroadcastIfNeeded(id, instOptions); emitHandleMemOp(indir, id, (ins == INS_mulx) ? IF_RWR_RWR_ARD : emitInsModeFormat(ins, IF_RRD_RRD_ARD), ins); @@ -6871,11 +6923,7 @@ void emitter::emitIns_R_R_C(instruction ins, id->idReg1(reg1); id->idReg2(reg2); id->idAddr()->iiaFieldHnd = fldHnd; - if (instOptions == INS_OPTS_EVEX_b) - { - assert(UseEvexEncoding()); - id->idSetEvexbContext(); - } + SetEvexBroadcastIfNeeded(id, instOptions); UNATIVE_OFFSET sz = emitInsSizeCV(id, insCodeRM(ins)); id->idCodeSize(sz); @@ -6889,7 +6937,8 @@ void emitter::emitIns_R_R_C(instruction ins, * Add an instruction with three register operands. */ -void emitter::emitIns_R_R_R(instruction ins, emitAttr attr, regNumber targetReg, regNumber reg1, regNumber reg2) +void emitter::emitIns_R_R_R( + instruction ins, emitAttr attr, regNumber targetReg, regNumber reg1, regNumber reg2, insOpts instOptions) { assert(IsAvx512OrPriorInstruction(ins)); assert(IsThreeOperandAVXInstruction(ins) || IsKInstruction(ins)); @@ -6901,6 +6950,13 @@ void emitter::emitIns_R_R_R(instruction ins, emitAttr attr, regNumber targetReg, id->idReg2(reg1); id->idReg3(reg2); + if ((instOptions & INS_OPTS_b_MASK) != INS_OPTS_NONE) + { + // if EVEX.b needs to be set in this path, then it should be embedded rounding. + assert(UseEvexEncoding()); + id->idSetEvexbContext(instOptions); + } + UNATIVE_OFFSET sz = emitInsSizeRR(id, insCodeRM(ins)); id->idCodeSize(sz); @@ -6921,12 +6977,8 @@ void emitter::emitIns_R_R_S( id->idReg1(reg1); id->idReg2(reg2); id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs); + SetEvexBroadcastIfNeeded(id, instOptions); - if (instOptions == INS_OPTS_EVEX_b) - { - assert(UseEvexEncoding()); - id->idSetEvexbContext(); - } #ifdef DEBUG id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs; #endif @@ -8224,11 +8276,11 @@ void emitter::emitIns_SIMD_R_R_C(instruction ins, // op2Reg -- The register of the second operand // void emitter::emitIns_SIMD_R_R_R( - instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg) + instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, insOpts instOptions) { if (UseSimdEncoding()) { - emitIns_R_R_R(ins, attr, targetReg, op1Reg, op2Reg); + emitIns_R_R_R(ins, attr, targetReg, op1Reg, op2Reg, instOptions); } else { @@ -10656,7 +10708,7 @@ void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz) // void emitter::emitDispEmbBroadcastCount(instrDesc* id) { - if (!id->idIsEvexbContext()) + if (!id->idIsEvexbContextSet()) { return; } @@ -10665,6 +10717,37 @@ void emitter::emitDispEmbBroadcastCount(instrDesc* id) printf(" {1to%d}", vectorSize / baseSize); } +// emitDispEmbRounding: Display the tag where embedded rounding is activated +// +// Arguments: +// id - The instruction descriptor +// +void emitter::emitDispEmbRounding(instrDesc* id) +{ + if (!id->idIsEvexbContextSet()) + { + return; + } + assert(!id->idHasMem()); + unsigned roundingMode = id->idGetEvexbContext(); + if (roundingMode == 1) + { + printf(" {rd-sae}"); + } + else if (roundingMode == 2) + { + printf(" {ru-sae}"); + } + else if (roundingMode == 3) + { + printf(" {rz-sae}"); + } + else + { + unreached(); + } +} + //-------------------------------------------------------------------- // emitDispIns: Dump the given instruction to jitstdout. // @@ -11533,6 +11616,7 @@ void emitter::emitDispIns( printf("%s, ", emitRegName(id->idReg1(), attr)); printf("%s, ", emitRegName(reg2, attr)); printf("%s", emitRegName(reg3, attr)); + emitDispEmbRounding(id); break; } diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 82a83dd0f2e53c..05c18bdbff2fae 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -157,6 +157,8 @@ bool IsRedundantCmp(emitAttr size, regNumber reg1, regNumber reg2); bool AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, GenCondition cond); bool AreFlagsSetForSignJumpOpt(regNumber reg, emitAttr opSize, GenCondition cond); +insOpts GetEmbRoundingMode(uint8_t mode) const; + bool hasRexPrefix(code_t code) { #ifdef TARGET_AMD64 @@ -335,6 +337,25 @@ code_t AddSimdPrefixIfNeeded(const instrDesc* id, code_t code, emitAttr size) return code; } +//------------------------------------------------------------------------ +// SetEvexBroadcastIfNeeded: set embedded broadcast if needed. +// +// Arguments: +// id - instruction descriptor +// instOptions - emit options +void SetEvexBroadcastIfNeeded(instrDesc* id, insOpts instOptions) +{ + if ((instOptions & INS_OPTS_b_MASK) == INS_OPTS_EVEX_eb_er_rd) + { + assert(UseEvexEncoding()); + id->idSetEvexbContext(instOptions); + } + else + { + assert(instOptions == 0); + } +} + //------------------------------------------------------------------------ // AddSimdPrefixIfNeeded: Add the correct SIMD prefix. // Check if the prefix already exists befpre adding. @@ -627,7 +648,12 @@ void emitIns_R_R_S(instruction ins, int offs, insOpts instOptions = INS_OPTS_NONE); -void emitIns_R_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3); +void emitIns_R_R_R(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + regNumber reg3, + insOpts instOptions = INS_OPTS_NONE); void emitIns_R_R_A_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir, int ival, insFormat fmt); @@ -738,7 +764,12 @@ void emitIns_SIMD_R_R_C(instruction ins, CORINFO_FIELD_HANDLE fldHnd, int offs, insOpts instOptions = INS_OPTS_NONE); -void emitIns_SIMD_R_R_R(instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg); +void emitIns_SIMD_R_R_R(instruction ins, + emitAttr attr, + regNumber targetReg, + regNumber op1Reg, + regNumber op2Reg, + insOpts instOptions = INS_OPTS_NONE); void emitIns_SIMD_R_R_S(instruction ins, emitAttr attr, regNumber targetReg, @@ -897,7 +928,7 @@ inline bool emitIsUncondJump(instrDesc* jmp) // inline bool HasEmbeddedBroadcast(const instrDesc* id) const { - return id->idIsEvexbContext(); + return id->idIsEvexbContextSet(); } inline bool HasHighSIMDReg(const instrDesc* id) const; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 0902c00c1650c1..ac4514c6382036 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -556,6 +556,12 @@ enum GenTreeFlags : unsigned int GTF_MDARRLOWERBOUND_NONFAULTING = 0x20000000, // GT_MDARR_LOWER_BOUND -- An MD array lower bound operation that cannot fault. Same as GT_IND_NONFAULTING. + GTF_HW_ER_MASK = 0x30000000, // Bits used by handle types below + GTF_HW_ER_TO_EVEN = 0x00000000, // GT_HWINTRINSIC -- embedded rounding mode: FloatRoundingMode = ToEven (Default) "{rn-sae}" + GTF_HW_ER_TO_NEGATIVEINFINITY = 0x10000000, // GT_HWINTRINSIC -- embedded rounding mode: FloatRoundingMode = ToNegativeInfinity "{rd-sae}" + GTF_HW_ER_TO_POSITIVEINFINITY = 0x20000000, // GT_HWINTRINSIC -- embedded rounding mode: FloatRoundingMode = ToPositiveInfinity "{ru-sae}" + GTF_HW_ER_TO_ZERO = 0x30000000, // GT_HWINTRINSIC -- embedded rounding mode: FloatRoundingMode = ToZero "{rz-sae}" + }; inline constexpr GenTreeFlags operator ~(GenTreeFlags a) @@ -2225,6 +2231,43 @@ struct GenTree return (gtOper == GT_CNS_INT) ? (gtFlags & GTF_ICON_HDL_MASK) : GTF_EMPTY; } +#ifdef FEATURE_HW_INTRINSICS + + void ClearEmbRoundingMode() + { + assert(gtOper == GT_HWINTRINSIC); + gtFlags &= ~GTF_HW_ER_MASK; + } + // Set GenTreeFlags on HardwareIntrinsic node to specify the FloatRoundingMode. + // mode can be one of the values from System.Runtime.Intrinsics.X86.FloatRoundingMode. + void SetEmbRoundingMode(uint8_t mode) + { + assert(gtOper == GT_HWINTRINSIC); + ClearEmbRoundingMode(); + switch (mode) + { + case 0x09: + gtFlags |= GTF_HW_ER_TO_NEGATIVEINFINITY; + break; + case 0x0A: + gtFlags |= GTF_HW_ER_TO_POSITIVEINFINITY; + break; + case 0x0B: + gtFlags |= GTF_HW_ER_TO_ZERO; + break; + default: + break; + } + } + + uint8_t GetEmbRoundingMode() + { + assert(gtOper == GT_HWINTRINSIC); + return (uint8_t)((gtFlags & GTF_HW_ER_MASK) >> 28); + } + +#endif // FEATURE_HW_INTRINSICS + // Mark this node as no longer being a handle; clear its GTF_ICON_*_HDL bits. void ClearIconHandleMask() { @@ -6310,6 +6353,7 @@ struct GenTreeJitIntrinsic : public GenTreeMultiOp }; #ifdef FEATURE_HW_INTRINSICS + struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic { GenTreeHWIntrinsic(var_types type, diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index d3dc426133d36d..dcd5c86129b74d 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -201,8 +201,11 @@ enum HWIntrinsicFlag : unsigned int // The intrinsic is a PermuteVar2x intrinsic HW_Flag_PermuteVar2x = 0x4000000, - // The intrinsic is an embedded broadcast compatiable intrinsic + // The intrinsic is an embedded broadcast compatible intrinsic HW_Flag_EmbBroadcastCompatible = 0x8000000, + + // The intrinsic is an embedded rounding compatible intrinsic + HW_Flag_EmbRoundingCompatible = 0x10000000 #endif // TARGET_XARCH }; @@ -587,6 +590,27 @@ struct HWIntrinsicInfo HWIntrinsicFlag flags = lookupFlags(id); return (flags & HW_Flag_EmbBroadcastCompatible) != 0; } + + static bool IsEmbRoundingCompatible(NamedIntrinsic id) + { + HWIntrinsicFlag flags = lookupFlags(id); + return (flags & HW_Flag_EmbRoundingCompatible) != 0; + } + + static size_t EmbRoundingArgPos(NamedIntrinsic id) + { + // This helper function returns the expected position, + // where the embedded rounding control argument should be. + assert(IsEmbRoundingCompatible(id)); + switch (id) + { + case NI_AVX512F_Add: + return 3; + + default: + unreached(); + } + } #endif // TARGET_XARCH static bool IsMaybeCommutative(NamedIntrinsic id) diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index accf1fc62552d5..00342a2d820f35 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -333,6 +333,13 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) emit->emitIns_R_R(ins, simdSize, op1Reg, op2Reg); } } + else if (HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId) && !op3->IsCnsIntOrI()) + { + auto emitSwCase = [&](int8_t i) { genHWIntrinsic_R_R_RM(node, ins, simdSize, i); }; + regNumber baseReg = node->ExtractTempReg(); + regNumber offsReg = node->GetSingleTempReg(); + genHWIntrinsicJumpTableFallback(intrinsicId, op3Reg, baseReg, offsReg, emitSwCase); + } else { switch (intrinsicId) @@ -708,6 +715,31 @@ void CodeGen::genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, e genHWIntrinsic_R_R_RM(node, ins, attr, targetReg, op1Reg, op2); } +//------------------------------------------------------------------------ +// genHWIntrinsic_R_R_RM: Generates the code for a hardware intrinsic node that takes a register operand, a +// register/memory operand, and that returns a value in register +// +// Arguments: +// node - The hardware intrinsic node +// ins - The instruction being generated +// attr - The emit attribute for the instruction being generated +// ival - a "fake" immediate to indicate the rounding mode +// +void CodeGen::genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival) +{ + regNumber targetReg = node->GetRegNum(); + GenTree* op1 = node->Op(1); + GenTree* op2 = node->Op(2); + regNumber op1Reg = op1->GetRegNum(); + + assert(targetReg != REG_NA); + assert(op1Reg != REG_NA); + + node->SetEmbRoundingMode((uint8_t)ival); + + genHWIntrinsic_R_R_RM(node, ins, attr, targetReg, op1Reg, op2); +} + //------------------------------------------------------------------------ // genHWIntrinsic_R_R_RM: Generates the code for a hardware intrinsic node that takes a register operand, a // register/memory operand, and that returns a value in register @@ -733,6 +765,33 @@ void CodeGen::genHWIntrinsic_R_R_RM( } bool isRMW = node->isRMWHWIntrinsic(compiler); + + if (node->GetEmbRoundingMode() != 0) + { + // As embedded rounding only appies in R_R_R case, we can skip other checks for different paths. + OperandDesc op2Desc = genOperandDesc(op2); + assert(op2Desc.GetKind() == OperandKind::Reg); + regNumber op2Reg = op2Desc.GetReg(); + + if ((op1Reg != targetReg) && (op2Reg == targetReg) && isRMW) + { + // We have "reg2 = reg1 op reg2" where "reg1 != reg2" on a RMW instruction. + // + // For non-commutative instructions, we should have ensured that op2 was marked + // delay free in order to prevent it from getting assigned the same register + // as target. However, for commutative instructions, we can just swap the operands + // in order to have "reg2 = reg2 op reg1" which will end up producing the right code. + + op2Reg = op1Reg; + op1Reg = targetReg; + } + + uint8_t mode = node->GetEmbRoundingMode(); + insOpts instOptions = GetEmitter()->GetEmbRoundingMode(mode); + GetEmitter()->emitIns_SIMD_R_R_R(ins, attr, targetReg, op1Reg, op2Reg, instOptions); + return; + } + inst_RV_RV_TT(ins, attr, targetReg, op1Reg, op2, isRMW); } diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 8f6ed5f07c0d2a..893c9d011cf4b8 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -829,7 +829,7 @@ HARDWARE_INTRINSIC(AVX2, Xor, // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // AVX512F Intrinsics HARDWARE_INTRINSIC(AVX512F, Abs, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pabsd, INS_invalid, INS_vpabsq, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512F, Add, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_paddd, INS_paddd, INS_paddq, INS_paddq, INS_addps, INS_addpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible) +HARDWARE_INTRINSIC(AVX512F, Add, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_paddd, INS_paddd, INS_paddq, INS_paddq, INS_addps, INS_addpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, AlignRight32, 64, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_valignd, INS_valignd, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, AlignRight64, 64, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_valignq, INS_valignq, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, And, 64, 2, true, {INS_pand, INS_pand, INS_pand, INS_pand, INS_pand, INS_pand, INS_vpandq, INS_vpandq, INS_andps, INS_andpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible) diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index 97cb490052bb00..bfa71ddc491a54 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -285,6 +285,16 @@ CORINFO_InstructionSet HWIntrinsicInfo::lookupIsa(const char* className, const c // int HWIntrinsicInfo::lookupImmUpperBound(NamedIntrinsic id) { + if (HWIntrinsicInfo::IsEmbRoundingCompatible(id)) + { + // The only case this branch should be hit is that JIT is generating a jump table fallback when the + // FloatRoundingMode is not a compile-time constant. + // Although the expected FloatRoundingMode values are 8, 9, 10, 11, but in the generated jump table, results for + // entries within [0, 11] are all calculated, + // Any unexpected value, say [0, 7] should be blocked by the managed code. + return 11; + } + assert(HWIntrinsicInfo::lookupCategory(id) == HW_Category_IMM); switch (id) diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index e03c68df7c10fe..473e38df0a8706 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -1239,7 +1239,7 @@ void CodeGen::inst_RV_RV_TT( bool IsEmbBroadcast = CodeGenInterface::IsEmbeddedBroadcastEnabled(ins, op2); if (IsEmbBroadcast) { - instOptions = INS_OPTS_EVEX_b; + instOptions = INS_OPTS_EVEX_eb_er_rd; if (emitter::IsBitwiseInstruction(ins) && varTypeIsLong(op2->AsHWIntrinsic()->GetSimdBaseType())) { switch (ins) @@ -1306,7 +1306,7 @@ void CodeGen::inst_RV_RV_TT( op1Reg = targetReg; } - emit->emitIns_SIMD_R_R_R(ins, size, targetReg, op1Reg, op2Reg); + emit->emitIns_SIMD_R_R_R(ins, size, targetReg, op1Reg, op2Reg, instOptions); } break; diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index 5fd9dd456d65cd..e9d8b461bf96d8 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -203,9 +203,15 @@ enum insFlags : uint64_t enum insOpts: unsigned { - INS_OPTS_NONE, + INS_OPTS_NONE = 0, + + INS_OPTS_EVEX_eb_er_rd = 1, // Embedded Broadcast or Round down + + INS_OPTS_EVEX_er_ru = 2, // Round up + + INS_OPTS_EVEX_er_rz = 3, // Round towards zero - INS_OPTS_EVEX_b + INS_OPTS_b_MASK = (INS_OPTS_EVEX_eb_er_rd | INS_OPTS_EVEX_er_ru | INS_OPTS_EVEX_er_rz), // mask for Evex.b related features. }; #elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 31e2a55e1b4862..0ff7658c2470fe 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -1067,6 +1067,68 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); + if (HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId)) + { + size_t numArgs = node->GetOperandCount(); + size_t expectedArgNum = HWIntrinsicInfo::EmbRoundingArgPos(intrinsicId); + if (numArgs == expectedArgNum) + { + CorInfoType simdBaseJitType = node->GetSimdBaseJitType(); + uint32_t simdSize = node->GetSimdSize(); + var_types simdType = node->TypeGet(); + GenTree* lastOp = node->Op(numArgs); + + if (lastOp->IsCnsIntOrI()) + { + uint8_t mode = static_cast(lastOp->AsIntCon()->IconValue()); + + GenTreeHWIntrinsic* embRoundingNode; + switch (numArgs) + { + case 3: + embRoundingNode = comp->gtNewSimdHWIntrinsicNode(simdType, node->Op(1), node->Op(2), + intrinsicId, simdBaseJitType, simdSize); + break; + case 2: + embRoundingNode = comp->gtNewSimdHWIntrinsicNode(simdType, node->Op(1), intrinsicId, + simdBaseJitType, simdSize); + break; + + default: + unreached(); + } + + embRoundingNode->SetEmbRoundingMode(mode); + BlockRange().InsertAfter(lastOp, embRoundingNode); + LIR::Use use; + if (BlockRange().TryGetUse(node, &use)) + { + use.ReplaceWith(embRoundingNode); + } + else + { + embRoundingNode->SetUnusedValue(); + } + BlockRange().Remove(node); + BlockRange().Remove(lastOp); + node = embRoundingNode; + if (mode != 0x08) + { + // As embedded rounding can only work under register-to-register form, we can skip contain check at + // this point. + return node->gtNext; + } + } + else + { + // If the control byte is not constant, generate a jump table fallback when emitting the code. + assert(!lastOp->IsCnsIntOrI()); + node->SetEmbRoundingMode(0x08); + return node->gtNext; + } + } + } + switch (intrinsicId) { case NI_Vector128_ConditionalSelect: diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 534c260a9a62cf..9912598f19553c 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -2142,6 +2142,13 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou } } + if (HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId) && + numArgs == HWIntrinsicInfo::EmbRoundingArgPos(intrinsicId) && !lastOp->IsCnsIntOrI()) + { + buildInternalIntRegisterDefForNode(intrinsicTree); + buildInternalIntRegisterDefForNode(intrinsicTree); + } + // Determine whether this is an RMW operation where op2+ must be marked delayFree so that it // is not allocated the same register as the target. bool isRMW = intrinsicTree->isRMWHWIntrinsic(compiler); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs index 86a472f71802a1..5e6bc6e2023ec4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs @@ -1338,6 +1338,11 @@ internal X64() { } /// public static Vector512 Add(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } /// + /// __m512d _mm512_add_pd (__m512d a, __m512d b) + /// VADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} + /// + public static Vector512 Add(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512 _mm512_add_ps (__m512 a, __m512 b) /// VADDPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst{er} /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs index 534120a3351561..fa0ebeaa816230 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs @@ -1339,6 +1339,11 @@ internal X64() { } /// public static Vector512 Add(Vector512 left, Vector512 right) => Add(left, right); /// + /// __m512d _mm512_add_pd (__m512d a, __m512d b) + /// VADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} + /// + public static Vector512 Add(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Add(left, right, mode); + /// /// __m512 _mm512_add_ps (__m512 a, __m512 b) /// VADDPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst{er} /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Enums.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Enums.cs index 527635bb58e992..ef5471d6ba43ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Enums.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Enums.cs @@ -165,4 +165,24 @@ public enum FloatComparisonMode : byte /// UnorderedTrueSignaling = 31, } + + public enum FloatRoundingMode : byte + { + /// + /// _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC + /// + ToEven = 0x08, + /// + /// _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC + /// + ToNegativeInfinity = 0x09, + /// + /// _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC + /// + ToPositiveInfinity = 0x0A, + /// + /// _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC + /// + ToZero = 0x0B, + } } 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 00318b5d15d389..b3db125c2c7ece 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -5192,6 +5192,7 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 Abs(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 Abs(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } @@ -6026,6 +6027,13 @@ public enum FloatComparisonMode : byte OrderedGreaterThanNonSignaling = (byte)30, UnorderedTrueSignaling = (byte)31, } + public enum FloatRoundingMode : byte + { + ToEven = 0x08, + ToNegativeInfinity = 0x09, + ToPositiveInfinity = 0x0A, + ToZero = 0x0B, + } [System.CLSCompliantAttribute(false)] public abstract partial class Fma : System.Runtime.Intrinsics.X86.Avx { diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs index 6343e12513b7c5..6e284032934299 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs @@ -1519,6 +1519,9 @@ ("SimpleBinOpTest.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Xor", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateFirstResult"] = "(ushort)(left[0] ^ right[0]) != result[0]", ["ValidateRemainingResults"] = "(ushort)(left[i] ^ right[i]) != result[i]"}), ("SimpleBinOpTest.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Xor", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateFirstResult"] = "(uint)(left[0] ^ right[0]) != result[0]", ["ValidateRemainingResults"] = "(uint)(left[i] ^ right[i]) != result[i]"}), ("SimpleBinOpTest.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Xor", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateFirstResult"] = "(ulong)(left[0] ^ right[0]) != result[0]", ["ValidateRemainingResults"] = "(ulong)(left[i] ^ right[i]) != result[i]"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), }; (string templateFileName, Dictionary templateData)[] Avx512F_ScalarUpperInputs = new [] @@ -2631,6 +2634,12 @@ void ProcessInput(StreamWriter testListFile, string groupName, (string templateF testName += ".Tuple3Op"; suffix += "Tuple3Op"; } + else if (input.templateFileName == "SimpleBinOpEmbRounding.template") + { + testName += ".EmbeddedRounding"; + testName += $".{input.templateData["RoundingMode"]}"; + suffix += "EmbeddedRounding"; + } var fileName = Path.Combine(outputDirectory, $"{testName}.cs"); diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template new file mode 100644 index 00000000000000..da78721c596d01 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template @@ -0,0 +1,315 @@ +// 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\X86\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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Collections.Generic; +using System.Runtime.Intrinsics.X86; +using Xunit; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + [Fact] + public static void {Method}{RetBaseType}{RoundingMode}() + { + var test = new BinaryOpTest__{Method}{RetBaseType}{RoundingMode}(); + + 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 basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // 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 BinaryOpTest__{Method}{RetBaseType}{RoundingMode} + { + 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] = ({Op1BaseType}){FixedInput1}; } + 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] = ({Op2BaseType}){FixedInput2}; } + 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(BinaryOpTest__{Method}{RetBaseType}{RoundingMode} testClass) + { + var result = {Isa}.{Method}(_fld1, _fld2, FloatRoundingMode.{RoundingMode}); + + 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}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + + private SimpleBinaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}, {Op2BaseType}> _dataTable; + + public BinaryOpTest__{Method}{RetBaseType}{RoundingMode}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput1}; } + 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] = ({Op2BaseType}){FixedInput2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = ({Op2BaseType}){FixedInput2}; } + _dataTable = new SimpleBinaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}, {Op2BaseType}>(_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), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_LoadAligned)); + + var result = {Isa}.{Method}( + {LoadIsa}.LoadAligned{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.LoadAligned{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _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}>) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr), + FloatRoundingMode.{RoundingMode} + }); + + 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, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, op2, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, _fld2, FloatRoundingMode.{RoundingMode}); + + 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, FloatRoundingMode.{RoundingMode}); + + 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 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, {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; + + for (int i = 0; i < result.Length; i++) + { + ulong[] answerTable = binaryEmbRoundingAnswerTable[("{RetBaseType}", "{Method}", "{RoundingMode}")]; + + if (BitConverter.{CastingMethod}(result[i]) != answerTable[i]) + { + succeeded = false; + Console.WriteLine("Avx512 {Method} Embedded rounding failed on {RetBaseType} with {RoundingMode}:"); + foreach (var item in result) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + 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; + } + } + + private static Dictionary<(string, string, string), ulong[]> binaryEmbRoundingAnswerTable = new Dictionary<(string, string, string), ulong[]> + { + {("Double", "Add", "ToNegativeInfinity"), new ulong[] {0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000}}, + {("Double", "Add", "ToPositiveInfinity"), new ulong[] {0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001}}, + {("Double", "Add", "ToZero"), new ulong[] {0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000}}, + }; + } +} From b8294daf4bfa75fb2e33d7d732dc6df2cdc78974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Fri, 19 Jan 2024 19:25:36 +0100 Subject: [PATCH 131/189] Remove xunit-specific Rosyln analyzer testing packages (#97220) Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.Xunit brings in an older xunit reference that is not compatible in some cases, we've hit issues with it while upgrading our xunit reference in the past. As mentioned in https://github.com/dotnet/roslyn-sdk/issues/1099#issuecomment-1723487931 we can use the generic testing package instead. The only difference is the exception type thrown when an assert fails, e.g. the test throws `InvalidOperationException` instead of `Xunit.Sdk.EqualException` but we still get actual vs. expected exception messages so this is enough for us. --- .../CSharpAnalyzerVerifier`1+Test.cs | 3 +-- .../CSharpAnalyzerVerifier`1.cs | 7 +++---- .../IntrinsicsInSystemPrivateCoreLib.Tests.csproj | 6 +++--- .../JSImportGenerator.Unit.Tests.csproj | 4 ++-- .../ComInterfaceGenerator.Unit.Tests.csproj | 6 +++--- .../tests/Common/Verifiers/CSharpAnalyzerVerifier.cs | 8 ++++---- .../tests/Common/Verifiers/CSharpCodeFixVerifier.cs | 10 ++++------ .../Common/Verifiers/CSharpSourceGeneratorVerifier.cs | 3 +-- .../LibraryImportGenerator.Unit.Tests.csproj | 6 +++--- .../tests/FunctionalTests/CSharpCodeFixVerifier`2.cs | 3 +-- .../System.Text.RegularExpressions.Tests.csproj | 2 +- .../ILLink.RoslynAnalyzer.Tests.csproj | 4 ++-- .../Verifiers/CSharpAnalyzerVerifier`1.cs | 9 ++++----- .../Verifiers/CSharpCodeFixVerifier`2.cs | 3 +-- 14 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs index fb8a08a8c49054..73b0db166d8c07 100644 --- a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs @@ -1,14 +1,13 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; namespace IntrinsicsInSystemPrivateCoreLib.Test { public static partial class CSharpAnalyzerVerifier where TAnalyzer : DiagnosticAnalyzer, new() { - public class Test : CSharpAnalyzerTest + public class Test : CSharpAnalyzerTest { public Test() { diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs index 6677ef554399b9..03c25089895e19 100644 --- a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs @@ -2,7 +2,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; using System.Threading; using System.Threading.Tasks; @@ -13,15 +12,15 @@ public static partial class CSharpAnalyzerVerifier { /// public static DiagnosticResult Diagnostic() - => CSharpAnalyzerVerifier.Diagnostic(); + => CSharpAnalyzerVerifier.Diagnostic(); /// public static DiagnosticResult Diagnostic(string diagnosticId) - => CSharpAnalyzerVerifier.Diagnostic(diagnosticId); + => CSharpAnalyzerVerifier.Diagnostic(diagnosticId); /// public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) - => CSharpAnalyzerVerifier.Diagnostic(descriptor); + => CSharpAnalyzerVerifier.Diagnostic(descriptor); /// public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj index 62859b1fbbad02..594def676a1ad0 100644 --- a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj index 70e76ba4e5842f..076244c304bb81 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/JSImportGenerator.UnitTest/JSImportGenerator.Unit.Tests.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj index beda4af3a72283..635669006eb258 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj @@ -25,9 +25,9 @@ - - - + + + diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs index 08e29e3a5a2fb3..79c12e1d0de624 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Testing.XUnit; +using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; @@ -15,15 +15,15 @@ public static class CSharpAnalyzerVerifier { /// public static DiagnosticResult Diagnostic() - => AnalyzerVerifier.Diagnostic(); + => CSharpAnalyzerVerifier.Diagnostic(); /// public static DiagnosticResult Diagnostic(string diagnosticId) - => AnalyzerVerifier.Diagnostic(diagnosticId); + => CSharpAnalyzerVerifier.Diagnostic(diagnosticId); /// public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) - => AnalyzerVerifier.Diagnostic(descriptor); + => CSharpAnalyzerVerifier.Diagnostic(descriptor); /// public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs index b63dffca7fa66e..cc815fb1c24959 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs @@ -10,10 +10,8 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Testing; -using Microsoft.CodeAnalysis.CSharp.Testing.XUnit; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; namespace Microsoft.Interop.UnitTests.Verifiers { @@ -23,15 +21,15 @@ public static class CSharpCodeFixVerifier { /// public static DiagnosticResult Diagnostic() - => CodeFixVerifier.Diagnostic(); + => CSharpCodeFixVerifier.Diagnostic(); /// public static DiagnosticResult Diagnostic(string diagnosticId) - => CodeFixVerifier.Diagnostic(diagnosticId); + => CSharpCodeFixVerifier.Diagnostic(diagnosticId); /// public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) - => CodeFixVerifier.Diagnostic(descriptor); + => CSharpCodeFixVerifier.Diagnostic(descriptor); /// /// Create a with the diagnostic message created with the provided arguments. @@ -109,7 +107,7 @@ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] ex await test.RunAsync(CancellationToken.None); } - internal class Test : CSharpCodeFixTest + internal class Test : CSharpCodeFixTest { public Test() { diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs index 84bd897adc61f0..55b90860b42609 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; namespace Microsoft.Interop.UnitTests.Verifiers { @@ -79,7 +78,7 @@ public static async Task VerifySourceGeneratorAsync(string[] sources, params Dia await test.RunAsync(CancellationToken.None); } - internal class Test : CSharpSourceGeneratorTest + internal class Test : CSharpSourceGeneratorTest { public Test(TestTargetFramework targetFramework) { diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj index 085cea14f22549..7574caa597790f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj @@ -40,9 +40,9 @@ - - - + + + diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CSharpCodeFixVerifier`2.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CSharpCodeFixVerifier`2.cs index 1c51e2fc444791..54220dda61244e 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CSharpCodeFixVerifier`2.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/CSharpCodeFixVerifier`2.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; namespace System.Text.RegularExpressions.Tests { @@ -33,7 +32,7 @@ public static async Task VerifyCodeFixAsync(string source, string fixedSource, R await test.RunAsync(CancellationToken.None); } - public class Test : CSharpCodeFixTest + public class Test : CSharpCodeFixTest { public Test(ReferenceAssemblies? references = null) { diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj index 613d15b7883f1e..dd8f9603cd7198 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj @@ -72,7 +72,7 @@ - + - - + + diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs index 3f8aa28a86996c..d5a520afc2d8e6 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; using Microsoft.CodeAnalysis.Text; namespace ILLink.RoslynAnalyzer.Tests @@ -25,18 +24,18 @@ public static partial class CSharpAnalyzerVerifier { /// public static DiagnosticResult Diagnostic () - => CSharpAnalyzerVerifier.Diagnostic (); + => CSharpAnalyzerVerifier.Diagnostic (); /// public static DiagnosticResult Diagnostic (string diagnosticId) - => CSharpAnalyzerVerifier.Diagnostic (diagnosticId); + => CSharpAnalyzerVerifier.Diagnostic (diagnosticId); /// public static DiagnosticResult Diagnostic (DiagnosticDescriptor descriptor) - => CSharpAnalyzerVerifier.Diagnostic (descriptor); + => CSharpAnalyzerVerifier.Diagnostic (descriptor); public static DiagnosticResult Diagnostic (DiagnosticId diagnosticId) - => CSharpAnalyzerVerifier.Diagnostic (DiagnosticDescriptors.GetDiagnosticDescriptor (diagnosticId)); + => CSharpAnalyzerVerifier.Diagnostic (DiagnosticDescriptors.GetDiagnosticDescriptor (diagnosticId)); /// public static async Task VerifyAnalyzerAsync ( diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs index b541b06db52f6e..808fc4b772884f 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; namespace ILLink.RoslynAnalyzer.Tests { @@ -25,7 +24,7 @@ public partial class CSharpCodeFixVerifier where TAnalyzer : DiagnosticAnalyzer, new() where TCodeFix : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, new() { - public class Test : CSharpCodeFixTest + public class Test : CSharpCodeFixTest { public Test () { From 24f9706ad654bf23e966c04c85c0dafb96a85316 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 19 Jan 2024 10:32:25 -0800 Subject: [PATCH 132/189] JIT: remove size contribution to perf score (#97069) Keep the perf score a pure performance metric. Size is emitted as a separate metric, so clients interested in a metric that is some combination of size and speed can do their own computations based on these two values. Resolves #96878. --- src/coreclr/jit/codegencommon.cpp | 8 -------- src/coreclr/jit/emit.h | 5 ----- 2 files changed, 13 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index f0471d242d812c..0bd567916f468a 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2002,14 +2002,6 @@ void CodeGen::genEmitMachineCode() compiler->compCodeGenDone = true; #endif -#if defined(DEBUG) || defined(LATE_DISASM) - // Add code size information into the Perf Score - // All compPerfScore calculations must be performed using doubles - compiler->info.compPerfScore += ((double)compiler->info.compTotalHotCodeSize * (double)PERFSCORE_CODESIZE_COST_HOT); - compiler->info.compPerfScore += - ((double)compiler->info.compTotalColdCodeSize * (double)PERFSCORE_CODESIZE_COST_COLD); -#endif // DEBUG || LATE_DISASM - if (compiler->opts.disAsm && compiler->opts.disTesting) { printf("; END METHOD %s\n", compiler->eeGetMethodFullName(compiler->info.compMethodHnd)); diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 08d91386d8bb22..5d629deaf95e19 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1891,11 +1891,6 @@ class emitter #define PERFSCORE_MEMORY_WRITE 2 #define PERFSCORE_MEMORY_READ_WRITE 3 -#define PERFSCORE_CODESIZE_COST_HOT 0.10f -#define PERFSCORE_CODESIZE_COST_COLD 0.01f - -#define PERFSCORE_CALLEE_SPILL_COST 0.75f - struct insExecutionCharacteristics { float insThroughput; From b157e9c38f68a12c15953109233bf437b82901ce Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 19:56:17 +0100 Subject: [PATCH 133/189] [main] Update dependencies from 7 repositories (#96678) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/msquic build 20240104.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24054.1 * Update dependencies from https://github.com/dotnet/icu build 20240110.4 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24060.4 * Update dependencies from https://github.com/dotnet/xharness build 20240108.2 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24058.2 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240110.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24060.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240104.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24054.1 * Update dependencies from https://github.com/dotnet/cecil build 20240102.1 Microsoft.DotNet.Cecil From Version 0.11.4-alpha.23617.1 -> To Version 0.11.4-alpha.24052.1 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/icu build 20240110.4 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24060.4 * Update dependencies from https://github.com/dotnet/xharness build 20240110.2 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24060.2 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240110.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24060.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240110.4 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24060.4 * Update dependencies from https://github.com/dotnet/cecil build 20240102.1 Microsoft.DotNet.Cecil From Version 0.11.4-alpha.23617.1 -> To Version 0.11.4-alpha.24052.1 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/icu build 20240110.4 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24060.4 * Update dependencies from https://github.com/dotnet/xharness build 20240111.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24061.4 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240110.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24060.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240110.4 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24060.4 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/icu build 20240110.4 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24060.4 * Update dependencies from https://github.com/dotnet/xharness build 20240111.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24061.4 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240110.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24060.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240110.4 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24060.4 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/icu build 20240110.4 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24060.4 * Update dependencies from https://github.com/dotnet/xharness build 20240111.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24061.4 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240110.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24060.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240110.4 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24060.4 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/icu build 20240110.4 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24060.4 * Update dependencies from https://github.com/dotnet/xharness build 20240111.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24061.4 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240110.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24060.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240110.4 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24060.4 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/runtime build 20240114.3 Microsoft.DotNet.ILCompiler , Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Text.Json From Version 9.0.0-alpha.1.23617.5 -> To Version 9.0.0-alpha.1.24064.3 * Update dependencies from https://github.com/dotnet/icu build 20240115.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24065.1 * Update dependencies from https://github.com/dotnet/xharness build 20240115.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24065.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240115.2 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24065.2 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240115.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23611.1 -> To Version 9.0.0-alpha.0.24065.1 * Update dependencies from https://github.com/dotnet/cecil build 20240115.1 Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24052.1 -> To Version 0.11.4-alpha.24065.1 * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Update dependencies from https://github.com/dotnet/icu build 20240115.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24065.1 * Update dependencies from https://github.com/dotnet/xharness build 20240116.3 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24066.3 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240117.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24067.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240115.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23618.4 -> To Version 9.0.0-alpha.0.24065.1 * Update dependencies from https://github.com/dotnet/cecil build 20240115.1 Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24052.1 -> To Version 0.11.4-alpha.24065.1 * Remove explicit Microsoft.Extensions.Logging references in WasmSymbolicator * Update dependencies from https://github.com/dotnet/msquic build 20240109.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24059.1 * Set XHarnessTargetFramework * Update dependencies from https://github.com/dotnet/icu build 20240115.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24065.1 * Update dependencies from https://github.com/dotnet/xharness build 20240116.3 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24066.3 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240117.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24067.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240115.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23618.4 -> To Version 9.0.0-alpha.0.24065.1 * Update dependencies from https://github.com/dotnet/cecil build 20240115.1 Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24052.1 -> To Version 0.11.4-alpha.24065.1 * Update dependencies from https://github.com/dotnet/msquic build 20240117.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24067.1 * Revert xharness updates * Fixup * Update dependencies from https://github.com/dotnet/icu build 20240118.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-alpha.1.23611.1 -> To Version 9.0.0-alpha.1.24068.1 * Update dependencies from https://github.com/dotnet/xharness build 20240116.3 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.23611.1 -> To Version 9.0.0-prerelease.24066.3 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240118.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.23615.1 -> To Version 9.0.0-beta.24068.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240115.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.23618.4 -> To Version 9.0.0-alpha.0.24065.1 * Update dependencies from https://github.com/dotnet/cecil build 20240115.1 Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24052.1 -> To Version 0.11.4-alpha.24065.1 * Update dependencies from https://github.com/dotnet/msquic build 20240117.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.23605.1 -> To Version 9.0.0-alpha.1.24067.1 * Revert "Update dependencies from https://github.com/dotnet/xharness build 20240116.3" This reverts commit 4911e32fefd7b049432cb311e6d0ef85db54fc78. --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Viktor Hofer Co-authored-by: Alexander Köplinger --- eng/Version.Details.xml | 96 +++++++++---------- eng/Versions.props | 46 ++++----- global.json | 2 +- .../wasm/symbolicator/WasmSymbolicator.csproj | 2 - 4 files changed, 72 insertions(+), 74 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ce1513c9b772eb..7cef0e251bb632 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,12 +1,12 @@ - + https://github.com/dotnet/icu - 69f5d6cef6a89b997a78d40bd6233bb8e208b1bb + 48829dd3ba47887db7fae2cd5f90cfc255883135 - + https://github.com/dotnet/msquic - 431215fa2edd1bff8486117cccdb1e29b8049f37 + 3fb2583170384341dbbc444cd5bb3d2319433fb6 https://github.com/dotnet/wcf @@ -89,9 +89,9 @@ a045dd54a4c44723c215d992288160eb1401bb7f - + https://github.com/dotnet/cecil - 81facb3f6009be2cdce70df30452bb75e9a8f993 + b8c2293cd1cbd9d0fe6f32d7b5befbd526b5a175 @@ -188,57 +188,57 @@ https://github.com/dotnet/arcade 3faeb9817f465151aa4bbcdb315f0a6170206760 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 https://github.com/dotnet/llvm-project @@ -296,30 +296,30 @@ https://github.com/dotnet/llvm-project d8bacb4031b1d1e290ab2792e8378560419ee0de - + https://github.com/dotnet/runtime - 4dffd80c4d77c27e772a0be26e8036af77fbb26e + 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 - + https://github.com/dotnet/runtime - 4dffd80c4d77c27e772a0be26e8036af77fbb26e + 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 - + https://github.com/dotnet/runtime - 4dffd80c4d77c27e772a0be26e8036af77fbb26e + 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 - + https://github.com/dotnet/runtime - 4dffd80c4d77c27e772a0be26e8036af77fbb26e + 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 - + https://github.com/dotnet/runtime - 4dffd80c4d77c27e772a0be26e8036af77fbb26e + 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 - + https://github.com/dotnet/runtime - 4dffd80c4d77c27e772a0be26e8036af77fbb26e + 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 https://github.com/dotnet/xharness @@ -353,13 +353,13 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 7064208ca24e7b40f2437f61e69701fb53d53cfa - + https://github.com/dotnet/hotreload-utils - 14a6f0b652e5d1053b5213adfcdfe1aaa6d52d6d + a59b2b3e33fdc7d64d298433f6195d92dde7cc76 - + https://github.com/dotnet/runtime-assets - 31e7ca5b1ba4328eb97b1b405a4a3719c1e2783f + 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 571d0bfcaa164b..9ababb1f455110 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -101,10 +101,10 @@ 6.0.0-preview.1.102 - 9.0.0-alpha.1.23617.5 + 9.0.0-alpha.1.24064.3 6.0.0 - 9.0.0-alpha.1.23617.5 + 9.0.0-alpha.1.24064.3 16.0.5-alpha.1.24060.4 16.0.5-alpha.1.24060.4 @@ -132,27 +132,27 @@ 5.0.0 5.0.0 7.0.0 - 9.0.0-alpha.1.23617.5 + 9.0.0-alpha.1.24064.3 6.0.0 7.0.0 4.5.4 4.5.0 - 9.0.0-alpha.1.23617.5 + 9.0.0-alpha.1.24064.3 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 - 9.0.0-beta.23615.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 + 9.0.0-beta.24068.1 1.0.0-prerelease.24056.4 1.0.0-prerelease.24056.4 @@ -182,7 +182,7 @@ 9.0.0-prerelease.23611.1 9.0.0-prerelease.23611.1 9.0.0-prerelease.23611.1 - 9.0.0-alpha.0.23618.4 + 9.0.0-alpha.0.24065.1 3.12.0 4.1.0 6.0.0 @@ -204,14 +204,14 @@ 8.0.0-preview-20230918.1 - 0.11.4-alpha.24052.1 + 0.11.4-alpha.24065.1 - 9.0.0-alpha.1.23617.5 + 9.0.0-alpha.1.24064.3 - 9.0.0-alpha.1.23611.1 + 9.0.0-alpha.1.24068.1 2.2.3 - 9.0.0-alpha.1.23605.1 + 9.0.0-alpha.1.24067.1 16.0.5-alpha.1.24060.4 16.0.5-alpha.1.24060.4 diff --git a/global.json b/global.json index 3f2d3ca7be9229..cd23531f6f7c43 100644 --- a/global.json +++ b/global.json @@ -13,6 +13,6 @@ "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.23607.2", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "9.0.0-alpha.1.23617.5" + "Microsoft.NET.Sdk.IL": "9.0.0-alpha.1.24064.3" } } diff --git a/src/mono/wasm/symbolicator/WasmSymbolicator.csproj b/src/mono/wasm/symbolicator/WasmSymbolicator.csproj index a21bb42d0506af..7b715fc9e3fbdf 100644 --- a/src/mono/wasm/symbolicator/WasmSymbolicator.csproj +++ b/src/mono/wasm/symbolicator/WasmSymbolicator.csproj @@ -9,8 +9,6 @@ - - From 0f07c3955378c2b8fd7e3ab05ecd6283aac403e3 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 19 Jan 2024 11:03:41 -0800 Subject: [PATCH 134/189] Fix phase order problem with throw helper blocks (#97201) Throw helper blocks are created in morph, then possibly removed if unnecessary in StackLevelSetter (under optimization). However, there was a case where StackLevelSetter removed an OVERFLOW throw helper block after optimization proved it unnecessary because of a constant zero dividend, but between StackLevelSetter and codegen, LSRA introduced a RELOAD node above the constant zero that `GenTree::CanDivOrModPossiblyOverflow()` didn't understand, thus causing it to think that overflow was possible. Codegen looked for the OVERFLOW throw helper block and couldn't find it. There are multiple fixes here, somewhat "defense in depth": - If `StackLevelSetter::SetThrowHelperBlocks()` determines a node can't throw divide-by-zero or ArithmeticException (overflow), it marks the node GTF_DIV_MOD_NO_BY_ZERO / GTF_DIV_MOD_NO_OVERFLOW, respectively. This is what morph does earlier in compilation. - `genMarkLabelsForCodegen()` does not mark throw helper blocks where `acdUsed` is false, to avoid marking deleted blocks. - More asserts are added that `acdUsed` is true when codegen goes to generate a branch to a throw helper. - `GenTree::OperExceptions` / `CanDivOrModPossiblyOverflow` are changed to skip COPY/RELOAD nodes. Fixes #96224 --- src/coreclr/jit/codegencommon.cpp | 11 ++++++++--- src/coreclr/jit/codegenloongarch64.cpp | 2 ++ src/coreclr/jit/codegenriscv64.cpp | 2 ++ src/coreclr/jit/compiler.h | 6 +++++- src/coreclr/jit/compiler.hpp | 4 ++-- src/coreclr/jit/emit.cpp | 10 +++++++++- src/coreclr/jit/flowgraph.cpp | 8 ++++---- src/coreclr/jit/gentree.cpp | 11 ++++------- src/coreclr/jit/gentree.h | 5 ++--- src/coreclr/jit/stacklevelsetter.cpp | 21 +++++++++++++++++++++ 10 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 0bd567916f468a..db035461a31369 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -455,10 +455,13 @@ void CodeGen::genMarkLabelsForCodegen() } // Walk all the exceptional code blocks and mark them, since they don't appear in the normal flow graph. - for (Compiler::AddCodeDsc* add = compiler->fgAddCodeList; add; add = add->acdNext) + for (Compiler::AddCodeDsc* add = compiler->fgAddCodeList; add != nullptr; add = add->acdNext) { - JITDUMP(" " FMT_BB " : throw helper block\n", add->acdDstBlk->bbNum); - add->acdDstBlk->SetFlags(BBF_HAS_LABEL); + if (add->acdUsed) + { + JITDUMP(" " FMT_BB " : throw helper block\n", add->acdDstBlk->bbNum); + add->acdDstBlk->SetFlags(BBF_HAS_LABEL); + } } for (EHblkDsc* const HBtab : EHClauses(compiler)) @@ -1521,6 +1524,7 @@ void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKi #ifdef DEBUG Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); + assert(add->acdUsed); assert(excpRaisingBlock == add->acdDstBlk); #if !FEATURE_FIXED_OUT_ARGS assert(add->acdStkLvlInit || isFramePointerUsed()); @@ -1533,6 +1537,7 @@ void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKi Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); PREFIX_ASSUME_MSG((add != nullptr), ("ERROR: failed to find exception throw block")); + assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; #if !FEATURE_FIXED_OUT_ARGS assert(add->acdStkLvlInit || isFramePointerUsed()); diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 0a15bdf1dc7c89..03471bc892f51c 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -7671,6 +7671,7 @@ inline void CodeGen::genJumpToThrowHlpBlk_la( #ifdef DEBUG Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); + assert(add->acdUsed); assert(excpRaisingBlock == add->acdDstBlk); #if !FEATURE_FIXED_OUT_ARGS assert(add->acdStkLvlInit || isFramePointerUsed()); @@ -7683,6 +7684,7 @@ inline void CodeGen::genJumpToThrowHlpBlk_la( Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); PREFIX_ASSUME_MSG((add != nullptr), ("ERROR: failed to find exception throw block")); + assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; #if !FEATURE_FIXED_OUT_ARGS assert(add->acdStkLvlInit || isFramePointerUsed()); diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index f3bc1b10c5bdb0..3e2788c385a733 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -7186,6 +7186,7 @@ inline void CodeGen::genJumpToThrowHlpBlk_la( #ifdef DEBUG Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); + assert(add->acdUsed); assert(excpRaisingBlock == add->acdDstBlk); #if !FEATURE_FIXED_OUT_ARGS assert(add->acdStkLvlInit || isFramePointerUsed()); @@ -7198,6 +7199,7 @@ inline void CodeGen::genJumpToThrowHlpBlk_la( Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); PREFIX_ASSUME_MSG((add != nullptr), ("ERROR: failed to find exception throw block")); + assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; #if !FEATURE_FIXED_OUT_ARGS assert(add->acdStkLvlInit || isFramePointerUsed()); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index cdff1b9fbfe0ae..7a9b480b91efc1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6566,7 +6566,11 @@ class Compiler struct AddCodeDsc { AddCodeDsc* acdNext; - BasicBlock* acdDstBlk; // block to which we jump + + // Initially the source block of the exception. After fgCreateThrowHelperBlocks, the block to which + // we jump to raise the exception. + BasicBlock* acdDstBlk; + unsigned acdData; SpecialCodeKind acdKind; // what kind of a special block is this? bool acdUsed; // do we need to keep this helper block? diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 4f10b648efb549..a686de150d5fe0 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3045,7 +3045,7 @@ inline bool Compiler::fgIsThrowHlpBlk(BasicBlock* block) // under stress, with implausible flow graph optimizations. So, walk the fgAddCodeList // for the final determination. - for (AddCodeDsc* add = fgAddCodeList; add; add = add->acdNext) + for (AddCodeDsc* add = fgAddCodeList; add != nullptr; add = add->acdNext) { if (block == add->acdDstBlk) { @@ -3068,7 +3068,7 @@ inline bool Compiler::fgIsThrowHlpBlk(BasicBlock* block) inline unsigned Compiler::fgThrowHlpBlkStkLevel(BasicBlock* block) { - for (AddCodeDsc* add = fgAddCodeList; add; add = add->acdNext) + for (AddCodeDsc* add = fgAddCodeList; add != nullptr; add = add->acdNext) { if (block == add->acdDstBlk) { diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 89b2b983f1711b..b993c29e1dda31 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -4299,7 +4299,15 @@ void emitter::emitDispJumpList() else #endif // TARGET_ARM64 { - printf(" -> IG%02u", ((insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel))->igNum); + insGroup* targetGroup = (insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel); + if (targetGroup == nullptr) + { + printf(" -> ILLEGAL"); + } + else + { + printf(" -> IG%02u", targetGroup->igNum); + } } if (jmp->idjShort) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 7966292d7fff30..1b33f593581335 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3539,15 +3539,15 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks() } //------------------------------------------------------------------------ -// fgFindExcptnTarget: finds the block to jump that will throw a given kind of exception +// fgFindExcptnTarget: finds the block to jump to that will throw a given kind of exception // // Arguments: -// kind - kind of exception to throw +// kind -- kind of exception to throw // refData -- bbThrowIndex of the block that will jump to the throw helper // // Return Value: // Code descriptor for the appropriate throw helper block, or nullptr if no such -// descriptor exists +// descriptor exists. // Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, unsigned refData) { @@ -3559,7 +3559,7 @@ Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, unsigne if (add == nullptr) { - // We should't be asking for these blocks late in compilation + // We shouldn't be asking for these blocks late in compilation // unless we know there are entries to be found. assert(!fgRngChkThrowAdded); } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 0ad8c48afc106b..ac88ed8abae8b9 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6908,7 +6908,6 @@ bool GenTree::OperIsImplicitIndir() const //------------------------------------------------------------------------------ // OperExceptions: Get exception set this tree may throw. // -// // Arguments: // comp - Compiler instance // @@ -6945,11 +6944,9 @@ ExceptionSetFlags GenTree::OperExceptions(Compiler* comp) ExceptionSetFlags exSetFlags = ExceptionSetFlags::None; - GenTree* op2 = this->gtGetOp2(); - - if (!(this->gtFlags & GTF_DIV_MOD_NO_BY_ZERO) && !op2->IsNeverZero()) + if (!(this->gtFlags & GTF_DIV_MOD_NO_BY_ZERO) && !this->gtGetOp2()->gtSkipReloadOrCopy()->IsNeverZero()) { - exSetFlags = ExceptionSetFlags::DivideByZeroException; + exSetFlags |= ExceptionSetFlags::DivideByZeroException; } if (this->OperIs(GT_DIV, GT_MOD) && this->CanDivOrModPossiblyOverflow(comp)) @@ -27217,8 +27214,8 @@ bool GenTree::CanDivOrModPossiblyOverflow(Compiler* comp) const if (this->gtFlags & GTF_DIV_MOD_NO_OVERFLOW) return false; - GenTree* op1 = this->gtGetOp1(); - GenTree* op2 = this->gtGetOp2(); + GenTree* op1 = this->gtGetOp1()->gtSkipReloadOrCopy(); + GenTree* op2 = this->gtGetOp2()->gtSkipReloadOrCopy(); // If the divisor is known to never be '-1', we cannot overflow. if (op2->IsNeverNegativeOne(comp)) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index ac4514c6382036..62f5e642ccddcb 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -8954,7 +8954,6 @@ inline bool GenTree::OperIsCopyBlkOp() // long constants in a target-independent way. inline bool GenTree::IsIntegralConst(ssize_t constVal) const - { if ((gtOper == GT_CNS_INT) && (AsIntConCommon()->IconValue() == constVal)) { @@ -9372,9 +9371,9 @@ inline GenTree* GenTree::gtCommaStoreVal() inline GenTree* GenTree::gtSkipReloadOrCopy() { // There can be only one reload or copy (we can't have a reload/copy of a reload/copy) - if (gtOper == GT_RELOAD || gtOper == GT_COPY) + if (OperIs(GT_RELOAD, GT_COPY)) { - assert(gtGetOp1()->OperGet() != GT_RELOAD && gtGetOp1()->OperGet() != GT_COPY); + assert(!gtGetOp1()->OperIs(GT_RELOAD, GT_COPY)); return gtGetOp1(); } return this; diff --git a/src/coreclr/jit/stacklevelsetter.cpp b/src/coreclr/jit/stacklevelsetter.cpp index 4f37e1621e6fd5..d422b6559632cb 100644 --- a/src/coreclr/jit/stacklevelsetter.cpp +++ b/src/coreclr/jit/stacklevelsetter.cpp @@ -80,6 +80,14 @@ PhaseStatus StackLevelSetter::DoPhase() madeChanges = true; } } + else + { + // Mark all the throw helpers as used to avoid asserts later. + for (Compiler::AddCodeDsc* add = comp->fgGetAdditionalCodeDescriptors(); add != nullptr; add = add->acdNext) + { + add->acdUsed = true; + } + } // The above loop might have moved a BBJ_COND's true target to its next block. // In such cases, reverse the condition so we can remove a branch. @@ -190,6 +198,7 @@ void StackLevelSetter::ProcessBlock(BasicBlock* block) // Arguments: // node - the node to process; // block - the source block for the node. +// void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block) { assert(node->OperMayThrow(comp)); @@ -227,11 +236,21 @@ void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block) { SetThrowHelperBlock(SCK_DIV_BY_ZERO, block); } + else + { + // Even if we thought it might divide by zero during morph, now we know it never will. + node->gtFlags |= GTF_DIV_MOD_NO_BY_ZERO; + } if ((exSetFlags & ExceptionSetFlags::ArithmeticException) != ExceptionSetFlags::None) { SetThrowHelperBlock(SCK_ARITH_EXCPN, block); } + else + { + // Even if we thought it might overflow during morph, now we know it never will. + node->gtFlags |= GTF_DIV_MOD_NO_OVERFLOW; + } } break; #endif @@ -256,10 +275,12 @@ void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block) // Arguments: // kind - the special throw-helper kind; // block - the source block that targets helper. +// void StackLevelSetter::SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* block) { Compiler::AddCodeDsc* add = comp->fgFindExcptnTarget(kind, comp->bbThrowIndex(block)); assert(add != nullptr); + // We expect we'll actually need this helper. add->acdUsed = true; From b4488eca7f87cde6f2a19d388b79a723d9ec71e8 Mon Sep 17 00:00:00 2001 From: Vitek Karas <10670590+vitek-karas@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:45:16 -0800 Subject: [PATCH 135/189] NativeAOT implement warning as errors as a compiler feature (#96567) Both roslyn and trimmer implement "warn as error" as a compiler feature - specifically the `TreatWarningsAsErrors` property only affects tools which explicitly react to it. There's a related MSBuild command line option which will affect all warnings, but that's not the one VS sets from the UI and what most customers use. See the comments in https://github.com/dotnet/runtime/issues/94178. This implements the same behavior as the trimmer. It adds 3 new command line options to `ilc` and enable "warn as error" globally and to enable/disable specific warning codes. Note that `illink` implements a more complex command line parsing where order of parameters matters and sometimes the later ones can override the earlier ones. This was trying to emulate `csc` behavior. For `ilc` I don't see a reason to emulate that because running `ilc` on its own is not supported, only going through the SDK, which will never pass the new parameters multiple times. For testing this enables the existing trimmer tests for this functionality. This uncovered a small issue in substitution parser, which is also fixed here. For simplicity the test infra emulates the `illink` command line behavior over `ilc` (which is easy as they're very similar). --- .../Microsoft.NETCore.Native.targets | 4 ++ .../Compiler/BodySubstitutionParser.cs | 8 +++- .../ILCompiler.Compiler/Compiler/Logger.cs | 24 +++++++---- .../Compiler/Logging/MessageContainer.cs | 4 +- .../TestCasesRunner/ILCompilerOptions.cs | 3 ++ .../TrimmingArgumentBuilder.cs | 42 +++++++++++++++++++ .../TestCasesRunner/TrimmingDriver.cs | 25 +++++++++-- .../aot/ILCompiler/ILCompilerRootCommand.cs | 9 ++++ src/coreclr/tools/aot/ILCompiler/Program.cs | 12 +++++- src/tests/Directory.Build.targets | 2 + .../TrimmingBehaviors/DeadCodeElimination.cs | 2 + .../SmokeTests/UnitTests/Devirtualization.cs | 2 + .../SmokeTests/UnitTests/Generics.cs | 5 +++ .../SmokeTests/UnitTests/Interfaces.cs | 6 +++ .../WarningsTests.g.cs | 6 +++ .../Warnings/CanWarnAsError.cs | 1 - .../Warnings/CanWarnAsErrorGlobal.cs | 39 +++++++++++++++++ .../Warnings/CanWarnAsErrorSubstitutions.xml | 4 +- 18 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorGlobal.cs diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index c9ff464334c490..c2039ec502d1f8 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -28,6 +28,7 @@ The .NET Foundation licenses this file to you under the MIT license. true true false + $(TreatWarningsAsErrors) <_IsiOSLikePlatform Condition="'$(_targetOS)' == 'maccatalyst' or $(_targetOS.StartsWith('ios')) or $(_targetOS.StartsWith('tvos'))">true <_IsApplePlatform Condition="'$(_targetOS)' == 'osx' or '$(_IsiOSLikePlatform)' == 'true'">true @@ -268,6 +269,9 @@ The .NET Foundation licenses this file to you under the MIT license. + + + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs index 8903274162f53d..979ade817b3d27 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs @@ -72,11 +72,15 @@ protected override void ProcessMethod(TypeDesc type, XPathNavigator methodNav, o _methodSubstitutions.Add(method, BodySubstitution.ThrowingBody); break; case "stub": - BodySubstitution stubBody; + BodySubstitution stubBody = null; if (method.Signature.ReturnType.IsVoid) stubBody = BodySubstitution.EmptyBody; else - stubBody = BodySubstitution.Create(TryCreateSubstitution(method.Signature.ReturnType, GetAttribute(methodNav, "value"))); + { + object substitution = TryCreateSubstitution(method.Signature.ReturnType, GetAttribute(methodNav, "value")); + if (substitution != null) + stubBody = BodySubstitution.Create(substitution); + } if (stubBody != null) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs index aa7a3ccad51f1b..182d84900f57e8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs @@ -34,6 +34,9 @@ public class Logger private readonly HashSet _trimWarnedAssemblies = new HashSet(StringComparer.OrdinalIgnoreCase); private readonly HashSet _aotWarnedAssemblies = new HashSet(StringComparer.OrdinalIgnoreCase); + private readonly bool _treatWarningsAsErrors; + private readonly Dictionary _warningsAsErrors; + public static Logger Null = new Logger(new TextLogWriter(TextWriter.Null), null, false); public bool IsVerbose { get; } @@ -46,7 +49,9 @@ public Logger( bool singleWarn, IEnumerable singleWarnEnabledModules, IEnumerable singleWarnDisabledModules, - IEnumerable suppressedCategories) + IEnumerable suppressedCategories, + bool treatWarningsAsErrors, + IDictionary warningsAsErrors) { _logWriter = writer; IsVerbose = isVerbose; @@ -55,17 +60,19 @@ public Logger( _singleWarnEnabledAssemblies = new HashSet(singleWarnEnabledModules, StringComparer.OrdinalIgnoreCase); _singleWarnDisabledAssemblies = new HashSet(singleWarnDisabledModules, StringComparer.OrdinalIgnoreCase); _suppressedCategories = new HashSet(suppressedCategories, StringComparer.Ordinal); + _treatWarningsAsErrors = treatWarningsAsErrors; + _warningsAsErrors = new Dictionary(warningsAsErrors); _compilerGeneratedState = ilProvider == null ? null : new CompilerGeneratedState(ilProvider, this); _unconditionalSuppressMessageAttributeState = new UnconditionalSuppressMessageAttributeState(_compilerGeneratedState, this); } - public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose, IEnumerable suppressedWarnings, bool singleWarn, IEnumerable singleWarnEnabledModules, IEnumerable singleWarnDisabledModules, IEnumerable suppressedCategories) - : this(new TextLogWriter(writer), ilProvider, isVerbose, suppressedWarnings, singleWarn, singleWarnEnabledModules, singleWarnDisabledModules, suppressedCategories) + public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose, IEnumerable suppressedWarnings, bool singleWarn, IEnumerable singleWarnEnabledModules, IEnumerable singleWarnDisabledModules, IEnumerable suppressedCategories, bool treatWarningsAsErrors, IDictionary warningsAsErrors) + : this(new TextLogWriter(writer), ilProvider, isVerbose, suppressedWarnings, singleWarn, singleWarnEnabledModules, singleWarnDisabledModules, suppressedCategories, treatWarningsAsErrors, warningsAsErrors) { } public Logger(ILogWriter writer, ILProvider ilProvider, bool isVerbose) - : this(writer, ilProvider, isVerbose, Array.Empty(), singleWarn: false, Array.Empty(), Array.Empty(), Array.Empty()) + : this(writer, ilProvider, isVerbose, Array.Empty(), singleWarn: false, Array.Empty(), Array.Empty(), Array.Empty(), false, new Dictionary()) { } @@ -155,10 +162,13 @@ internal bool IsWarningSuppressed(int code, MessageOrigin origin) return _unconditionalSuppressMessageAttributeState.IsSuppressed(code, origin); } - internal static bool IsWarningAsError(int _/*code*/) + internal bool IsWarningAsError(int warningCode) { - // TODO: warnaserror - return false; + bool value; + if (_treatWarningsAsErrors) + return !_warningsAsErrors.TryGetValue(warningCode, out value) || value; + + return _warningsAsErrors.TryGetValue(warningCode, out value) && value; } internal bool IsSingleWarn(ModuleDesc owningModule, string messageSubcategory) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/MessageContainer.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/MessageContainer.cs index 0d7f824599a002..78a1f8da4429c7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/MessageContainer.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/MessageContainer.cs @@ -131,7 +131,7 @@ internal static MessageContainer CreateErrorMessage(MessageOrigin? origin, Diagn if (TryLogSingleWarning(context, code, origin, subcategory)) return null; - if (Logger.IsWarningAsError(code)) + if (context.IsWarningAsError(code)) return new MessageContainer(MessageCategory.WarningAsError, text, code, subcategory, origin); return new MessageContainer(MessageCategory.Warning, text, code, subcategory, origin); @@ -148,7 +148,7 @@ internal static MessageContainer CreateErrorMessage(MessageOrigin? origin, Diagn if (TryLogSingleWarning(context, (int)id, origin, subcategory)) return null; - if (Logger.IsWarningAsError((int)id)) + if (context.IsWarningAsError((int)id)) return new MessageContainer(MessageCategory.WarningAsError, id, subcategory, origin, args); return new MessageContainer(MessageCategory.Warning, id, subcategory, origin, args); diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs index d7ae07095635aa..e23c1902ee5ae0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ILCompilerOptions.cs @@ -16,5 +16,8 @@ public class ILCompilerOptions public List Descriptors = new List (); public bool FrameworkCompilation; public bool SingleWarn; + public List SubstitutionFiles = new List (); + public bool TreatWarningsAsErrors; + public Dictionary WarningsAsErrors = new Dictionary (); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs index b32391dad4b178..1d075aeffeb354 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingArgumentBuilder.cs @@ -147,6 +147,7 @@ public virtual void AddStripLinkAttributes (bool stripLinkAttributes) public virtual void AddSubstitutions (string file) { + Options.SubstitutionFiles.Add (file); } public virtual void AddLinkAttributes (string file) @@ -161,6 +162,30 @@ public virtual void AddAdditionalArgument (string flag, string[] values) else if (flag == "--singlewarn") { Options.SingleWarn = true; } + else if (flag.StartsWith("--warnaserror")) + { + if (flag == "--warnaserror" || flag == "--warnaserror+") + { + if (values.Length == 0) + Options.TreatWarningsAsErrors = true; + else + { + foreach (int warning in ProcessWarningCodes(values)) + Options.WarningsAsErrors[warning] = true; + } + + } + else if (flag == "--warnaserror-") + { + if (values.Length == 0) + Options.TreatWarningsAsErrors = false; + else + { + foreach (int warning in ProcessWarningCodes(values)) + Options.WarningsAsErrors[warning] = false; + } + } + } } public virtual void ProcessTestInputAssembly (NPath inputAssemblyPath) @@ -261,6 +286,23 @@ private static void AppendExpandedPaths (Dictionary dictionary, } } + private static readonly char[] s_separator = new char[] { ',', ';', ' ' }; + + private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) + { + foreach (string value in warningCodes) + { + string[] values = value.Split(s_separator, StringSplitOptions.RemoveEmptyEntries); + foreach (string id in values) + { + if (!id.StartsWith("IL", StringComparison.Ordinal) || !ushort.TryParse(id.AsSpan(2), out ushort code)) + continue; + + yield return code; + } + } + } + public ILCompilerOptions Build () { var options = Options; diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs index f141e8e9982120..9000ee5b2475c5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/TrimmingDriver.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Xml; using ILCompiler; using ILCompiler.Dataflow; using ILLink.Shared.TrimAnalysis; @@ -84,15 +85,33 @@ public ILScanResults Trim (ILCompilerOptions options, TrimmingCustomizations? cu options.SingleWarn, singleWarnEnabledModules: Enumerable.Empty (), singleWarnDisabledModules: Enumerable.Empty (), - suppressedCategories: Enumerable.Empty ()); + suppressedCategories: Enumerable.Empty (), + treatWarningsAsErrors: options.TreatWarningsAsErrors, + warningsAsErrors: options.WarningsAsErrors); foreach (var descriptor in options.Descriptors) { if (!File.Exists (descriptor)) throw new FileNotFoundException ($"'{descriptor}' doesn't exist"); compilationRoots.Add (new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode (descriptor)); } - - ilProvider = new FeatureSwitchManager (ilProvider, logger, options.FeatureSwitches, default); + + var featureSwitches = options.FeatureSwitches; + BodyAndFieldSubstitutions substitutions = default; + IReadOnlyDictionary>? resourceBlocks = default; + foreach (string substitutionFilePath in options.SubstitutionFiles) + { + using FileStream fs = File.OpenRead(substitutionFilePath); + substitutions.AppendFrom(BodySubstitutionsParser.GetSubstitutions( + logger, typeSystemContext, XmlReader.Create(fs), substitutionFilePath, featureSwitches)); + + fs.Seek(0, SeekOrigin.Begin); + + resourceBlocks = ManifestResourceBlockingPolicy.UnionBlockings(resourceBlocks, + ManifestResourceBlockingPolicy.SubstitutionsReader.GetSubstitutions( + logger, typeSystemContext, XmlReader.Create(fs), substitutionFilePath, featureSwitches)); + } + + ilProvider = new FeatureSwitchManager(ilProvider, logger, featureSwitches, substitutions); CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState (ilProvider, logger); diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 84f9c5acd94443..917f1b612942ed 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -129,6 +129,12 @@ internal sealed class ILCompilerRootCommand : CliRootCommand new("--singlewarnassembly") { DefaultValueFactory = _ => Array.Empty(), Description = "Generate single AOT/trimming warning for given assembly" }; public CliOption SingleWarnDisabledAssemblies { get; } = new("--nosinglewarnassembly") { DefaultValueFactory = _ => Array.Empty(), Description = "Expand AOT/trimming warnings for given assembly" }; + public CliOption TreatWarningsAsErrors { get; } = + new("--warnaserror") { Description = "Treat warnings as errors" }; + public CliOption WarningsAsErrorsEnable { get; } = + new("--warnaserr") { Description = "Enable treating specific warnings as errors" }; + public CliOption WarningsAsErrorsDisable { get; } = + new("--nowarnaserr") { Description = "Disable treating specific warnings as errors" }; public CliOption DirectPInvokes { get; } = new("--directpinvoke") { DefaultValueFactory = _ => Array.Empty(), Description = "PInvoke to call directly" }; public CliOption DirectPInvokeLists { get; } = @@ -225,6 +231,9 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") Options.Add(NoAotWarn); Options.Add(SingleWarnEnabledAssemblies); Options.Add(SingleWarnDisabledAssemblies); + Options.Add(TreatWarningsAsErrors); + Options.Add(WarningsAsErrorsEnable); + Options.Add(WarningsAsErrorsDisable); Options.Add(DirectPInvokes); Options.Add(DirectPInvokeLists); Options.Add(MaxGenericCycleDepth); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 0ff5cd87b5ffeb..09ec67cfd2a14f 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -75,8 +75,18 @@ public int Run() ILProvider ilProvider = new NativeAotILProvider(); + Dictionary warningsAsErrors = new Dictionary(); + foreach (int warning in ProcessWarningCodes(Get(_command.WarningsAsErrorsEnable))) + { + warningsAsErrors[warning] = true; + } + foreach (int warning in ProcessWarningCodes(Get(_command.WarningsAsErrorsDisable))) + { + warningsAsErrors[warning] = false; + } var logger = new Logger(Console.Out, ilProvider, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), - Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies), suppressedWarningCategories); + Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies), suppressedWarningCategories, + Get(_command.TreatWarningsAsErrors), warningsAsErrors); // NativeAOT is full AOT and its pre-compiled methods can not be // thrown away at runtime if they mismatch in required ISAs or diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 4352e3a798d7ca..e49657c6b81467 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -555,6 +555,8 @@ so the default for the test tree is partial. --> partial + false + $(CoreCLRILCompilerDir) $(CoreCLRCrossILCompilerDir) $(CoreCLRILCompilerDir)netstandard/ILCompiler.Build.Tasks.dll diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs index 21cc8c1fb63c56..d54c801ccee3e9 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DeadCodeElimination.cs @@ -651,6 +651,8 @@ private static void ThrowIfPresent(Type testType, string typeName) } } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "That's the point")] private static void ThrowIfPresentWithUsableMethodTable(Type testType, string typeName) { Type t = GetTypeSecretly(testType, typeName); diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs index 5954c6118e6696..f2c70d7b1ee60a 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; class Devirtualization { @@ -103,6 +104,7 @@ static void AssertEqual(T expected, T actual) [MethodImpl(MethodImplOptions.NoInlining)] static void TestIntf2(IIntf2 o, int expected) => AssertEqual(expected, o.GetValue()); + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { TestIntf1(new Intf1Impl(), 123); diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs index 7b414ac3dd1837..145d7b3d0c3ea9 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs @@ -5,7 +5,12 @@ using System.Reflection; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +[UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "MakeGenericType - Intentional")] +[UnconditionalSuppressMessage("Trimming", "IL2060", Justification = "MakeGenericMethod - Intentional")] +[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType/MakeGenericMethod - Intentional")] +[UnconditionalSuppressMessage("AOT", "IL3054", Justification = "Generic expansion aborted - Intentional")] class Generics { internal static int Run() diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs index 6c70d13c31a5f4..4f34959be84b3b 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; public class Interfaces { @@ -1225,6 +1226,7 @@ public static int CallIndirect() static Type s_fooType = typeof(Foo); + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { Type t = typeof(FrobCaller<>).MakeGenericType(s_fooType); @@ -1289,6 +1291,7 @@ class Atom { } static Type s_atomType = typeof(Atom); + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { Type t = typeof(Wrapper<>).MakeGenericType(s_atomType); @@ -1354,6 +1357,7 @@ class Atom : AtomBase { } static Type s_atomType = typeof(Atom); static Type s_atomBaseType = typeof(AtomBase); + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { Type t = typeof(FrobCaller<,>).MakeGenericType(typeof(AbjectFail), s_atomType); @@ -1576,6 +1580,7 @@ class Gen where T : IFoo public static string GrabCookie() => T.ImHungryGiveMeCookie(); } + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { var r = (string)typeof(Gen<>).MakeGenericType(typeof(Baz)).GetMethod("GrabCookie").Invoke(null, Array.Empty()); @@ -1612,6 +1617,7 @@ class Gen where T : IFoo public static string GrabCookie() => T.ImHungryGiveMeCookie(); } + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { Activator.CreateInstance(typeof(Baz<>).MakeGenericType(typeof(Atom1))); diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/WarningsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/WarningsTests.g.cs index 63a5e67da42a3b..02236f3c852342 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/WarningsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/WarningsTests.g.cs @@ -87,6 +87,12 @@ public Task CanWarnAsError () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task CanWarnAsErrorGlobal () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InvalidWarningVersion () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsError.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsError.cs index 64990286095572..9c53c77bcc9f37 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsError.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsError.cs @@ -4,7 +4,6 @@ namespace Mono.Linker.Tests.Cases.Warnings { [ExpectNonZeroExitCode (1)] - [IgnoreTestCase ("Ignore in NativeAOT, see https://github.com/dotnet/runtime/issues/82447", IgnoredBy = Tool.NativeAot)] [SkipKeptItemsValidation] [SetupLinkerSubstitutionFile ("CanWarnAsErrorSubstitutions.xml")] [SetupLinkerArgument ("--verbose")] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorGlobal.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorGlobal.cs new file mode 100644 index 00000000000000..19ce617a2ed354 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorGlobal.cs @@ -0,0 +1,39 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Warnings +{ + [ExpectNonZeroExitCode (1)] + [SkipKeptItemsValidation] + [Define("IN_TEST_BUILD")] + [SetupLinkerSubstitutionFile ("CanWarnAsErrorSubstitutions.xml")] + [SetupLinkerArgument ("--verbose")] + [SetupLinkerArgument ("--warnaserror")] + [LogContains ("error IL2007")] + [LogContains ("error IL2008")] + [LogContains ("error IL2009")] + [LogContains ("error IL2010")] + [LogContains ("error IL2011")] + [LogContains ("error IL2012")] + [NoLinkedOutput] + public class CanWarnAsErrorGlobal + { + public static void Main () + { + } + } + +#if IN_TEST_BUILD + public class CanWarnAsError + { + class HelperClass + { + private int helperField = 0; + int HelperMethod () + { + return 0; + } + } + } +#endif +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorSubstitutions.xml b/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorSubstitutions.xml index 16862b155a6140..30efc508cdda9b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorSubstitutions.xml +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Warnings/CanWarnAsErrorSubstitutions.xml @@ -5,13 +5,13 @@ - + - + From 843316f41ae53e6ba1f323b9b5346e1801bd2f89 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 19 Jan 2024 21:13:52 +0100 Subject: [PATCH 136/189] JIT: Allow compacting loop header blocks (#97205) Minor diffs expected. Removes the last dependency on old loops (that I know of). --- src/coreclr/jit/fgopt.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 996ed5dd16f0e6..16ef174b53c63f 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -945,12 +945,6 @@ bool Compiler::fgCanCompactBlocks(BasicBlock* block, BasicBlock* bNext) return false; } - // Don't compact away any loop entry blocks that we added in optCanonicalizeLoops - if (block->HasFlag(BBF_OLD_LOOP_HEADER_QUIRK)) - { - return false; - } - // We don't want to compact blocks that are in different Hot/Cold regions // if (fgInDifferentRegions(block, bNext)) From 26d8316d162ac66395fba6be80e000c1c096ef5c Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 19 Jan 2024 21:59:16 +0100 Subject: [PATCH 137/189] JIT: Late expansion for profiled isinst (#97075) Co-authored-by: Jakob Botsch Nielsen --- src/coreclr/jit/assertionprop.cpp | 24 +- src/coreclr/jit/compiler.cpp | 3 + src/coreclr/jit/compiler.h | 14 ++ src/coreclr/jit/compphases.h | 1 + src/coreclr/jit/gentree.h | 6 +- src/coreclr/jit/helperexpansion.cpp | 355 ++++++++++++++++++++++++++++ src/coreclr/jit/importer.cpp | 11 +- src/coreclr/jit/morph.cpp | 14 +- 8 files changed, 411 insertions(+), 17 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 034bd7264bdd54..195e28ee845edb 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -4764,17 +4764,14 @@ GenTree* Compiler::optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCal { return optAssertionProp_Update(call, call, stmt); } - else if (!optLocalAssertionProp && (call->gtCallType == CT_HELPER)) - { - if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFINTERFACE) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFARRAY) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFCLASS) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_ISINSTANCEOFANY) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_CHKCASTINTERFACE) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_CHKCASTARRAY) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_CHKCASTCLASS) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_CHKCASTANY) || - call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_CHKCASTCLASS_SPECIAL)) + else if (!optLocalAssertionProp && call->IsHelperCall()) + { + const CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd); + if ((helper == CORINFO_HELP_ISINSTANCEOFINTERFACE) || (helper == CORINFO_HELP_ISINSTANCEOFARRAY) || + (helper == CORINFO_HELP_ISINSTANCEOFCLASS) || (helper == CORINFO_HELP_ISINSTANCEOFANY) || + (helper == CORINFO_HELP_CHKCASTINTERFACE) || (helper == CORINFO_HELP_CHKCASTARRAY) || + (helper == CORINFO_HELP_CHKCASTCLASS) || (helper == CORINFO_HELP_CHKCASTANY) || + (helper == CORINFO_HELP_CHKCASTCLASS_SPECIAL)) { GenTree* arg1 = call->gtArgs.GetArgByIndex(1)->GetNode(); if (arg1->gtOper != GT_LCL_VAR) @@ -4804,6 +4801,11 @@ GenTree* Compiler::optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCal return optAssertionProp_Update(arg1, call, stmt); } + + // TODO-InlineCast: check optAssertionIsNonNull for the object argument and replace + // the helper with its nonnull version, e.g.: + // CORINFO_HELP_ISINSTANCEOFANY -> CORINFO_HELP_ISINSTANCEOFANY_NONNULL + // so then fgLateCastExpansion can skip the null check. } } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e09aac3ac123cf..3efa0e844b0141 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5046,6 +5046,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // Expand thread local access DoPhase(this, PHASE_EXPAND_TLS, &Compiler::fgExpandThreadLocalAccess); + // Expand casts + DoPhase(this, PHASE_EXPAND_CASTS, &Compiler::fgLateCastExpansion); + // Insert GC Polls DoPhase(this, PHASE_INSERT_GC_POLLS, &Compiler::fgInsertGCPolls); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 7a9b480b91efc1..36c8725b3d143f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5832,6 +5832,9 @@ class Compiler bool fgVNBasedIntrinsicExpansionForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call); bool fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call); + PhaseStatus fgLateCastExpansion(); + bool fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call); + PhaseStatus fgInsertGCPolls(); BasicBlock* fgCreateGCPoll(GCPollType pollType, BasicBlock* block); @@ -7523,6 +7526,7 @@ class Compiler #define OMF_HAS_TLS_FIELD 0x00010000 // Method contains TLS field access #define OMF_HAS_SPECIAL_INTRINSICS 0x00020000 // Method contains special intrinsics expanded in late phases #define OMF_HAS_RECURSIVE_TAILCALL 0x00040000 // Method contains recursive tail call +#define OMF_HAS_EXPANDABLE_CAST 0x00080000 // Method contains casts eligible for late expansion // clang-format on @@ -7563,6 +7567,16 @@ class Compiler optMethodFlags |= OMF_HAS_STATIC_INIT; } + bool doesMethodHaveExpandableCasts() + { + return (optMethodFlags & OMF_HAS_EXPANDABLE_CAST) != 0; + } + + void setMethodHasExpandableCasts() + { + optMethodFlags |= OMF_HAS_EXPANDABLE_CAST; + } + bool doesMethodHaveGuardedDevirtualization() const { return (optMethodFlags & OMF_HAS_GUARDEDDEVIRT) != 0; diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h index a51ab4adc4f88e..23930985319769 100644 --- a/src/coreclr/jit/compphases.h +++ b/src/coreclr/jit/compphases.h @@ -98,6 +98,7 @@ CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2, f CompPhaseNameMacro(PHASE_STRESS_SPLIT_TREE, "Stress gtSplitTree", false, -1, false) CompPhaseNameMacro(PHASE_EXPAND_RTLOOKUPS, "Expand runtime lookups", false, -1, true) CompPhaseNameMacro(PHASE_EXPAND_STATIC_INIT, "Expand static init", false, -1, true) +CompPhaseNameMacro(PHASE_EXPAND_CASTS, "Expand casts", false, -1, true) CompPhaseNameMacro(PHASE_EXPAND_TLS, "Expand TLS access", false, -1, true) CompPhaseNameMacro(PHASE_INSERT_GC_POLLS, "Insert GC Polls", false, -1, true) CompPhaseNameMacro(PHASE_CREATE_THROW_HELPERS, "Create throw helper blocks", false, -1, true) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 62f5e642ccddcb..b8f109c68b43a8 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4123,6 +4123,7 @@ enum GenTreeCallFlags : unsigned int GTF_CALL_M_EXPANDED_EARLY = 0x00800000, // the Virtual Call target address is expanded and placed in gtControlExpr in Morph rather than in Lower GTF_CALL_M_HAS_LATE_DEVIRT_INFO = 0x01000000, // this call has late devirtualzation info GTF_CALL_M_LDVIRTFTN_INTERFACE = 0x02000000, // ldvirtftn on an interface type + GTF_CALL_M_CAST_CAN_BE_EXPANDED = 0x04000000, // this cast (helper call) can be expanded if it's profitable. To be removed. }; inline constexpr GenTreeCallFlags operator ~(GenTreeCallFlags a) @@ -5631,8 +5632,9 @@ struct GenTreeCall final : public GenTree CORINFO_CLASS_HANDLE gtRetClsHnd; // The return type handle of the call if it is a struct; always available union { - void* gtStubCallStubAddr; // GTF_CALL_VIRT_STUB - these are never inlined - CORINFO_CLASS_HANDLE gtInitClsHnd; // Used by static init helpers, represents a class they init + void* gtStubCallStubAddr; // GTF_CALL_VIRT_STUB - these are never inlined + CORINFO_CLASS_HANDLE gtInitClsHnd; // Used by static init helpers, represents a class they init + IL_OFFSET gtCastHelperILOffset; // Used by cast helpers to save corresponding IL offset }; union { diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 9b9fb009a6f50f..ac2a65b33ef95c 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -33,6 +33,55 @@ static GenTree* SpillExpression(Compiler* comp, GenTree* expr, BasicBlock* exprB return comp->gtNewLclVarNode(tmpNum); }; +//------------------------------------------------------------------------------ +// SplitAtTreeAndReplaceItWithLocal : Split block at the given tree and replace it with a local +// See comments in gtSplitTree and fgSplitBlockBeforeTree +// TODO: use this function in more places in this file. +// +// Arguments: +// comp - Compiler instance +// block - Block to split +// stmt - Statement containing the tree to split at +// tree - Tree to split at +// topBlock - [out] Top block after the split +// bottomBlock - [out] Bottom block after the split +// +// Return Value: +// Number of the local that replaces the tree +// +static unsigned SplitAtTreeAndReplaceItWithLocal( + Compiler* comp, BasicBlock* block, Statement* stmt, GenTree* tree, BasicBlock** topBlock, BasicBlock** bottomBlock) +{ + BasicBlock* prevBb = block; + GenTree** callUse = nullptr; + Statement* newFirstStmt = nullptr; + block = comp->fgSplitBlockBeforeTree(block, stmt, tree, &newFirstStmt, &callUse); + assert(prevBb != nullptr && block != nullptr); + + // Block ops inserted by the split need to be morphed here since we are after morph. + // We cannot morph stmt yet as we may modify it further below, and the morphing + // could invalidate callUse + while ((newFirstStmt != nullptr) && (newFirstStmt != stmt)) + { + comp->fgMorphStmtBlockOps(block, newFirstStmt); + newFirstStmt = newFirstStmt->GetNextStmt(); + } + + // Grab a temp to store the result. + const unsigned tmpNum = comp->lvaGrabTemp(true DEBUGARG("replacement local")); + comp->lvaTable[tmpNum].lvType = tree->TypeGet(); + + // Replace the original call with that temp + *callUse = comp->gtNewLclvNode(tmpNum, tree->TypeGet()); + + comp->fgMorphStmtBlockOps(block, stmt); + comp->gtUpdateStmtSideEffects(stmt); + + *topBlock = prevBb; + *bottomBlock = block; + return tmpNum; +} + //------------------------------------------------------------------------------ // gtNewRuntimeLookupHelperCallNode : Helper to create a runtime lookup call helper node. // @@ -1765,3 +1814,309 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock, JITDUMP("ReadUtf8: succesfully expanded!\n") return true; } + +//------------------------------------------------------------------------------ +// fgLateCastExpansion: Partially inline various cast helpers, e.g.: +// +// tmp = CORINFO_HELP_ISINSTANCEOFINTERFACE(clsHandle, obj); +// +// into: +// +// tmp = obj; +// if ((obj != null) && (obj->pMT != likelyClassHandle)) +// { +// tmp = CORINFO_HELP_ISINSTANCEOFINTERFACE(clsHandle, obj); +// } +// +// The goal is to move cast expansion logic from the importer to this phase, for now, +// this phase only supports "isinst" and for profiled casts only. +// +// Returns: +// PhaseStatus indicating what, if anything, was changed. +// +PhaseStatus Compiler::fgLateCastExpansion() +{ + if (!doesMethodHaveExpandableCasts()) + { + // Nothing to expand in the current method + return PhaseStatus::MODIFIED_NOTHING; + } + + if (!opts.IsOptimizedWithProfile()) + { + // Currently, we're only interested in expanding cast helpers using profile data + return PhaseStatus::MODIFIED_NOTHING; + } + + if (JitConfig.JitConsumeProfileForCasts() == 0) + { + return PhaseStatus::MODIFIED_NOTHING; + } + + const bool preferSize = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_SIZE_OPT); + if (preferSize) + { + // The optimization comes with a codegen size increase + JITDUMP("Optimized for size - bail out.\n"); + return PhaseStatus::MODIFIED_NOTHING; + } + return fgExpandHelper<&Compiler::fgLateCastExpansionForCall>(true); +} + +//------------------------------------------------------------------------------ +// PickLikelyClass: picks a likely class handle corresponding to the given IL offset +// +// Arguments: +// comp - Compiler instance +// offset - IL offset +// likelihood - [out] likelihood of the returned class +// +// Returns: +// Likely class handle or NO_CLASS_HANDLE +// +static CORINFO_CLASS_HANDLE PickLikelyClass(Compiler* comp, IL_OFFSET offset, unsigned* likelihood) +{ + // TODO-InlineCast: consider merging this helper with pickGDV + + const int maxLikelyClasses = 8; + LikelyClassMethodRecord likelyClasses[maxLikelyClasses]; + unsigned likelyClassCount = getLikelyClasses(likelyClasses, maxLikelyClasses, comp->fgPgoSchema, + comp->fgPgoSchemaCount, comp->fgPgoData, (int)offset); + + if (likelyClassCount == 0) + { + return NO_CLASS_HANDLE; + } + +#ifdef DEBUG + // Print all the candidates and their likelihoods to the log + for (UINT32 i = 0; i < likelyClassCount; i++) + { + const char* className = comp->eeGetClassName((CORINFO_CLASS_HANDLE)likelyClasses[i].handle); + JITDUMP(" %u) %p (%s) [likelihood:%u%%]\n", i + 1, likelyClasses[i].handle, className, + likelyClasses[i].likelihood); + } + + // Optional stress mode to pick a random known class, rather than + // the most likely known class. + if (JitConfig.JitRandomGuardedDevirtualization() != 0) + { + // Reuse the random inliner's random state. + CLRRandom* const random = + comp->impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization()); + unsigned index = static_cast(random->Next(static_cast(likelyClassCount))); + *likelihood = 100; + return (CORINFO_CLASS_HANDLE)likelyClasses[index].handle; + } +#endif + *likelihood = likelyClasses[0].likelihood; + return (CORINFO_CLASS_HANDLE)likelyClasses[0].handle; +} + +//------------------------------------------------------------------------------ +// fgLateCastExpansionForCall : Expand specific cast helper, see +// fgLateCastExpansion's comments. +// +// Arguments: +// block - Block containing the cast helper to expand +// stmt - Statement containing the cast helper +// call - The cast helper +// +// Returns: +// True if expanded, false otherwise. +// +bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call) +{ + if (!call->IsHelperCall() || !impIsCastHelperMayHaveProfileData(call->GetHelperNum())) + { + // Not a cast helper we're interested in + return false; + } + + if ((call->gtCallMoreFlags & GTF_CALL_M_CAST_CAN_BE_EXPANDED) == 0) + { + // It's not eligible for expansion (already expanded in importer) + // To be removed once we move cast expansion here completely. + return false; + } + + // Helper calls are never tail calls + assert(!call->IsTailCall()); + + BasicBlock* block = *pBlock; + JITDUMP("Attempting to expand a cast helper call in " FMT_BB "...\n", block->bbNum); + DISPTREE(call); + JITDUMP("\n"); + + // Currently, we only expand "isinst" and only using profile data. The long-term plan is to + // move cast expansion logic here from the importer completely. + unsigned likelihood = 100; + CORINFO_CLASS_HANDLE likelyCls = PickLikelyClass(this, call->gtCastHelperILOffset, &likelihood); + if (likelyCls == NO_CLASS_HANDLE) + { + // TODO: make null significant, so it could mean our object is likely just null + JITDUMP("Likely class is null - bail out.\n"); + return false; + } + + // if there is a dominating candidate with >= 50% likelihood, use it + const unsigned likelihoodMinThreshold = 50; + if (likelihood < likelihoodMinThreshold) + { + JITDUMP("Likely class likelihood is below %u%% - bail out.\n", likelihoodMinThreshold); + return false; + } + + // E.g. "call CORINFO_HELP_ISINSTANCEOFCLASS(class, obj)" + GenTree* clsArg = call->gtArgs.GetUserArgByIndex(0)->GetNode(); + CORINFO_CLASS_HANDLE expectedCls = gtGetHelperArgClassHandle(clsArg); + if (expectedCls == NO_CLASS_HANDLE) + { + // clsArg doesn't represent a class handle - bail out + // TODO-InlineCast: use VN if it's available (depends on when this phase is executed) + JITDUMP("clsArg is not a constant handle - bail out.\n"); + return false; + } + + const TypeCompareState castResult = info.compCompHnd->compareTypesForCast(likelyCls, expectedCls); + if (castResult == TypeCompareState::May) + { + JITDUMP("compareTypesForCast returned May for this candidate\n"); + return false; + } + + if ((info.compCompHnd->getClassAttribs(likelyCls) & (CORINFO_FLG_INTERFACE | CORINFO_FLG_ABSTRACT)) != 0) + { + // Possible scenario: someone changed Foo to be an interface, + // but static profile data still report it as a normal likely class. + JITDUMP("Likely class is abstract/interface - bail out (stale PGO data?).\n"); + return false; + } + + DebugInfo debugInfo = stmt->GetDebugInfo(); + + BasicBlock* firstBb; + BasicBlock* lastBb; + const unsigned tmpNum = SplitAtTreeAndReplaceItWithLocal(this, block, stmt, call, &firstBb, &lastBb); + lvaSetClass(tmpNum, expectedCls); + GenTree* tmpNode = gtNewLclvNode(tmpNum, call->TypeGet()); + *pBlock = lastBb; + + // We're going to expand this "isinst" like this: + // + // prevBb: [weight: 1.0] + // ... + // + // nullcheckBb (BBJ_COND): [weight: 1.0] + // tmp = obj; + // if (tmp == null) + // goto lastBlock; + // + // typeCheckBb (BBJ_COND): [weight: 0.5] + // if (tmp->pMT == likelyCls) + // goto typeCheckSucceedBb; + // + // fallbackBb (BBJ_ALWAYS): [weight: ] + // tmp = helper_call(expectedCls, obj); + // goto lastBlock; + // + // typeCheckSucceedBb (BBJ_ALWAYS): [weight: ] + // no-op (or tmp = null; in case of 'MustNot') + // + // lastBlock (BBJ_any): [weight: 1.0] + // use(tmp); + // + + // Block 1: nullcheckBb + // TODO-InlineCast: assertionprop should leave us a mark that objArg is never null, so we can omit this check + // it's too late to rely on upstream phases to do this for us (unless we do optRepeat). + GenTree* nullcheckOp = gtNewOperNode(GT_EQ, TYP_INT, tmpNode, gtNewNull()); + nullcheckOp->gtFlags |= GTF_RELOP_JMP_USED; + BasicBlock* nullcheckBb = fgNewBBFromTreeAfter(BBJ_COND, firstBb, gtNewOperNode(GT_JTRUE, TYP_VOID, nullcheckOp), + debugInfo, lastBb, true); + + // The very first statement in the whole expansion is to assign obj to tmp. + // We assume it's the value we're going to return in most cases. + GenTree* originalObj = gtCloneExpr(call->gtArgs.GetUserArgByIndex(1)->GetNode()); + Statement* assignTmp = fgNewStmtAtBeg(nullcheckBb, gtNewTempStore(tmpNum, originalObj), debugInfo); + gtSetStmtInfo(assignTmp); + fgSetStmtSeq(assignTmp); + + // Block 2: typeCheckBb + // TODO-InlineCast: if likelyCls == expectedCls we can consider saving to a local to re-use. + GenTree* likelyClsNode = gtNewIconEmbClsHndNode(likelyCls); + GenTree* mtCheck = gtNewOperNode(GT_EQ, TYP_INT, gtNewMethodTableLookup(gtCloneExpr(tmpNode)), likelyClsNode); + mtCheck->gtFlags |= GTF_RELOP_JMP_USED; + GenTree* jtrue = gtNewOperNode(GT_JTRUE, TYP_VOID, mtCheck); + BasicBlock* typeCheckBb = fgNewBBFromTreeAfter(BBJ_COND, nullcheckBb, jtrue, debugInfo, lastBb, true); + + // Block 3: fallbackBb + GenTree* fallbackTree = gtNewTempStore(tmpNum, call); + BasicBlock* fallbackBb = fgNewBBFromTreeAfter(BBJ_ALWAYS, typeCheckBb, fallbackTree, debugInfo, lastBb, true); + + // Block 4: typeCheckSucceedBb + GenTree* typeCheckSucceedTree; + if (castResult == TypeCompareState::MustNot) + { + // With TypeCompareState::MustNot it means our likely class never passes the type check. + // it means we just check obj's type for being likelyclass and return null if it's true. + typeCheckSucceedTree = gtNewTempStore(tmpNum, gtNewNull()); + } + else + { + // tmp is already assigned to obj, so we don't need to do anything here + // some downstream phase will collect this block. It's done for simplicity. + typeCheckSucceedTree = gtNewNothingNode(); + } + BasicBlock* typeCheckSucceedBb = + fgNewBBFromTreeAfter(BBJ_ALWAYS, fallbackBb, typeCheckSucceedTree, debugInfo, lastBb); + + // + // Wire up the blocks + // + firstBb->SetTarget(nullcheckBb); + nullcheckBb->SetTrueTarget(lastBb); + nullcheckBb->SetFalseTarget(typeCheckBb); + typeCheckBb->SetTrueTarget(typeCheckSucceedBb); + typeCheckBb->SetFalseTarget(fallbackBb); + fallbackBb->SetTarget(lastBb); + fgRemoveRefPred(lastBb, firstBb); + fgAddRefPred(nullcheckBb, firstBb); + fgAddRefPred(typeCheckBb, nullcheckBb); + fgAddRefPred(lastBb, nullcheckBb); + fgAddRefPred(fallbackBb, typeCheckBb); + fgAddRefPred(lastBb, typeCheckSucceedBb); + fgAddRefPred(typeCheckSucceedBb, typeCheckBb); + fgAddRefPred(lastBb, fallbackBb); + + // + // Re-distribute weights + // We assume obj is 50%/50% null/not-null (TODO: use profile data) + // and rely on profile for the slow path. + // + nullcheckBb->inheritWeight(firstBb); + typeCheckBb->inheritWeightPercentage(nullcheckBb, 50); + fallbackBb->inheritWeightPercentage(typeCheckBb, 100 - likelihood); + typeCheckSucceedBb->inheritWeightPercentage(typeCheckBb, likelihood); + lastBb->inheritWeight(firstBb); + + // + // Update bbNatLoopNum for all new blocks and validate EH regions + // + nullcheckBb->bbNatLoopNum = firstBb->bbNatLoopNum; + fallbackBb->bbNatLoopNum = firstBb->bbNatLoopNum; + typeCheckBb->bbNatLoopNum = firstBb->bbNatLoopNum; + typeCheckSucceedBb->bbNatLoopNum = firstBb->bbNatLoopNum; + assert(BasicBlock::sameEHRegion(firstBb, lastBb)); + assert(BasicBlock::sameEHRegion(firstBb, nullcheckBb)); + assert(BasicBlock::sameEHRegion(firstBb, fallbackBb)); + assert(BasicBlock::sameEHRegion(firstBb, typeCheckBb)); + + // Bonus step: merge prevBb with nullcheckBb as they are likely to be mergeable + if (fgCanCompactBlocks(firstBb, nullcheckBb)) + { + fgCompactBlocks(firstBb, nullcheckBb); + } + + return true; +} diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index a0981b548ecdf4..84e402a85b0ce8 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -5520,7 +5520,9 @@ GenTree* Compiler::impCastClassOrIsInstToTree( } // Check if this cast helper have some profile data - if (impIsCastHelperMayHaveProfileData(helper)) + // "isinst" with profile data is moved to a late phase. + // The long-term plan is to move all non-trivial expansions there. + if (impIsCastHelperMayHaveProfileData(helper) && isCastClass) { const int maxLikelyClasses = 32; LikelyClassMethodRecord likelyClasses[maxLikelyClasses]; @@ -5619,6 +5621,13 @@ GenTree* Compiler::impCastClassOrIsInstToTree( compCurBB->SetFlags(BBF_HAS_HISTOGRAM_PROFILE); } } + else if (!isCastClass && impIsCastHelperMayHaveProfileData(helper)) + { + // Maybe the late-cast-expand phase will have a better luck expanding this cast. + // TODO: enable for cast-class as well. + call->gtCallMoreFlags |= GTF_CALL_M_CAST_CAN_BE_EXPANDED; + call->gtCastHelperILOffset = ilOffset; + } return call; } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index d90e9f7870ec9e..ca7c46ef6654dd 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7697,10 +7697,18 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) optMethodFlags |= OMF_NEEDS_GCPOLLS; } - if (fgGlobalMorph && IsStaticHelperEligibleForExpansion(call)) + if (fgGlobalMorph) { - // Current method has potential candidates for fgExpandStaticInit phase - setMethodHasStaticInit(); + if (IsStaticHelperEligibleForExpansion(call)) + { + // Current method has potential candidates for fgExpandStaticInit phase + setMethodHasStaticInit(); + } + else if ((call->gtCallMoreFlags & GTF_CALL_M_CAST_CAN_BE_EXPANDED) != 0) + { + // Current method has potential candidates for fgLateCastExpansion phase + setMethodHasExpandableCasts(); + } } // Morph Type.op_Equality, Type.op_Inequality, and Enum.HasFlag From 993b23fc101a062be87de9dac21302034abd337d Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:24:02 +0100 Subject: [PATCH 138/189] Limit `PNSE` by using Invariant `HashCode` in `HybridGlobalization` (#96354) * Unblock all tests. * SortKey is Invariant now. * Missing SortKey changes + fix HashCode. * Typo * Revert unbocking tests connected with CompareOptions PNSE. * Hashing uses Invariant mode -these tests should be skipped. * Add active issue. * feedback * Feedback * Better documentation. * Add new tests + sanitize string before invariant comparison. * Comment + more cases. * Clean CI. * Missing change for clean CI commit. * `SortKey` not supported for non-invariant cultures, `HashCode` supported for non-invariant cultures only with `IgnoreCase` or `None` options. * Feedback. * Fix build, add docs. * Feedback @matouskozak @pavelsavara * Added tests + fixed algo. * Block failing tests for a follow-up PR. * Add more details to PNSE. * Feedback - correct comment --- .../features/globalization-hybrid-mode.md | 21 ++- .../tests/CollectionsTests.cs | 12 +- .../tests/LateBindingTests.cs | 4 +- .../CaseInsensitiveHashCodeProviderTests.cs | 11 +- .../tests/CollectionsUtilTests.cs | 6 +- .../tests/HashtableTests.cs | 4 +- ...meObjectCollectionBase.ConstructorTests.cs | 2 +- .../NameObjectCollectionBase.CopyToTests.cs | 4 +- ...eObjectCollectionBase.GetAllValuesTests.cs | 4 +- ...ObjectCollectionBase.GetEnumeratorTests.cs | 4 +- .../NameObjectCollectionBase.KeysTests.cs | 12 +- .../NameObjectCollectionBase.ReadOnlyTests.cs | 2 +- .../NameObjectCollectionBase.RemoveAtTests.cs | 6 +- .../NameObjectCollectionBase.SetItemTests.cs | 2 +- .../NameValueCollection.AddNVCTests.cs | 8 +- ...ameValueCollection.AddStringStringTests.cs | 6 +- .../NameValueCollection.ClearTests.cs | 2 +- .../NameValueCollection.CopyToTests.cs | 6 +- .../NameValueCollection.CtorTests.cs | 10 +- .../NameValueCollection.GetIntTests.cs | 2 +- .../NameValueCollection.GetItemTests.cs | 2 +- .../NameValueCollection.GetKeyTests.cs | 2 +- .../NameValueCollection.GetStringTests.cs | 2 +- .../NameValueCollection.GetValuesIntTests.cs | 2 +- ...ameValueCollection.GetValuesStringTests.cs | 2 +- .../NameValueCollection.RemoveTests.cs | 4 +- .../NameValueCollection.SetItemTests.cs | 10 +- .../NameValueCollection.SetTests.cs | 10 +- .../OrderedDictionaryTests.cs | 6 +- .../OutOfBoundsRegression.cs | 5 +- .../Mono/DataRowComparerTest.cs | 6 +- .../Mono/DataRowExtensionsTest.cs | 4 +- .../Mono/DataTableExtensionsTest.cs | 6 +- .../System/Data/DataRowComparerTests.cs | 12 +- .../System/Data/DataRowExtensionsTests.cs | 10 +- .../EnumerableRowCollectionExtensionsTests.cs | 10 +- .../Data/TypedTableBaseExtensionsTests.cs | 6 +- .../System/Data/DataTableExtensionsTest.cs | 2 +- .../tests/Functional/MailAddressTest.cs | 4 +- .../src/Resources/Strings.resx | 3 + .../System/Globalization/CompareInfo.Icu.cs | 42 ++++++ .../Globalization/CompareInfo.Invariant.cs | 10 ++ .../Globalization/CompareInfo.WebAssembly.cs | 67 +++++++++ .../src/System/Globalization/CompareInfo.cs | 24 +-- .../System.Runtime.Caching/MemoryCacheTest.cs | 46 ++---- .../GetStringComparerTests.cs | 2 +- .../CompareInfo/CompareInfoTests.HashCode.cs | 139 ++++++++++++++++++ .../CompareInfo/CompareInfoTests.cs | 36 ----- .../System.Globalization.Tests.csproj | 1 + .../System/StringComparer.cs | 7 +- .../System/StringComparerTests.cs | 4 +- .../System/StringGetHashCodeTests.cs | 5 +- .../System/StringTests.cs | 6 +- .../System/Text/RuneTests.cs | 3 +- .../CollectionTests.Specialized.Write.cs | 2 +- 55 files changed, 416 insertions(+), 214 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs diff --git a/docs/design/features/globalization-hybrid-mode.md b/docs/design/features/globalization-hybrid-mode.md index 9d993f7b31c825..001ae20002cc9e 100644 --- a/docs/design/features/globalization-hybrid-mode.md +++ b/docs/design/features/globalization-hybrid-mode.md @@ -17,12 +17,29 @@ For WebAssembly in Browser we are using Web API instead of some ICU data. Ideall Hybrid has higher priority than sharding or custom modes, described in globalization-icu-wasm.md. +**HashCode** + +Affected public APIs: +- System.Globalization.CompareInfo.GetHashCode + +For invariant culture all `CompareOptions` are available. + +For non-invariant cultures following `CompareOptions` are available: +- `CompareOption.None` +- `CompareOption.IgnoreCase` + +The remaining combinations for non-invariant cultures throw `PlatformNotSupportedException`. + **SortKey** Affected public APIs: - System.Globalization.CompareInfo.GetSortKey - System.Globalization.CompareInfo.GetSortKeyLength -- System.Globalization.CompareInfo.GetHashCode + +For invariant culture all `CompareOptions` are available. + +For non-invariant cultures `PlatformNotSupportedException` is thrown. + Indirectly affected APIs (the list might not be complete): - Microsoft.VisualBasic.Collection.Add - System.Collections.Hashtable.Add @@ -43,8 +60,6 @@ Indirectly affected APIs (the list might not be complete): - System.Net.Mail.MailAddress.GetHashCode - System.Xml.Xsl.XslCompiledTransform.Transform -Web API does not have an equivalent, so they throw `PlatformNotSupportedException`. - **Case change** Affected public APIs: diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/CollectionsTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/CollectionsTests.cs index 35195353a2ed6a..d387d548b41153 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/CollectionsTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/CollectionsTests.cs @@ -74,7 +74,7 @@ public static void Add_RelativeIndex() Assert.Equal(item1, coll[3]); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void Add_RelativeKey() { var coll = new Collection(); @@ -175,7 +175,7 @@ public static void RemoveAt_InvalidIndex_ThrowsArgumentOutOfRangeException() Assert.Throws("Index", () => coll.RemoveAt(-1)); // Index < 0 } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void Remove_Key() { var coll = CreateKeyedCollection(10); @@ -185,7 +185,7 @@ public static void Remove_Key() Assert.False(coll.Contains("Key3")); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void Remove_InvalidKey_ThrowsArgumentException() { var coll = CreateKeyedCollection(10); @@ -242,7 +242,7 @@ public static void Contains() Assert.False(coll.Contains(new Foo())); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void Contains_ByKey() { var coll = CreateKeyedCollection(10); @@ -275,7 +275,7 @@ public static void Item_Get_InvalidIndex_ThrowsIndexOutOfRangeException() Assert.Throws(() => coll[(object)Guid.Empty]); // Neither string nor int } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void Item_GetByKey() { Collection coll = CreateKeyedCollection(10); @@ -291,7 +291,7 @@ public static void Item_GetByKey() Assert.Equal(CreateValue(11), coll[(object)'X']); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void Item_GetByKey_InvalidIndex_ThrowsIndexOutOfRangeException() { Collection coll = CreateKeyedCollection(10); diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/LateBindingTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/LateBindingTests.cs index 3d66733224abea..a95f101731dfc0 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/LateBindingTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/LateBindingTests.cs @@ -32,7 +32,7 @@ public void LateSet(object obj, Type objType, string name, object[] args, string Assert.Equal(expected, getResult(obj)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(LateSetComplex_TestData))] public void LateSetComplex(object obj, Type objType, string name, object[] args, string[] paramNames, bool missing, bool valueType) { @@ -74,7 +74,7 @@ public void LateIndexSet(object obj, object[] args, string[] paramNames, Func("obj", () => new CaseInsensitiveHashCodeProvider().GetHashCode(null)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData("hello", "HELLO", true)] [InlineData("hello", "hello", true)] [InlineData("HELLO", "HELLO", true)] diff --git a/src/libraries/System.Collections.NonGeneric/tests/CollectionsUtilTests.cs b/src/libraries/System.Collections.NonGeneric/tests/CollectionsUtilTests.cs index 9b46116d50ed23..e5735bdbbbb7ef 100644 --- a/src/libraries/System.Collections.NonGeneric/tests/CollectionsUtilTests.cs +++ b/src/libraries/System.Collections.NonGeneric/tests/CollectionsUtilTests.cs @@ -8,7 +8,7 @@ namespace System.Collections.Tests { public static class CollectionsUtilTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void CreateCaseInsensitiveHashtable() { Hashtable hashtable = CollectionsUtil.CreateCaseInsensitiveHashtable(); @@ -20,7 +20,7 @@ public static void CreateCaseInsensitiveHashtable() AssertExtensions.Throws(null, () => hashtable.Add("key1", "value1")); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void CreateCaseInsensitiveHashtable_Capacity() { Hashtable hashtable = CollectionsUtil.CreateCaseInsensitiveHashtable(15); @@ -33,7 +33,7 @@ public static void CreateCaseInsensitiveHashtable_Capacity() AssertExtensions.Throws(null, () => hashtable.Add("key1", "value1")); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void CreateCaseInsensitiveHashtable_IDictionary() { Hashtable hashtable1 = CollectionsUtil.CreateCaseInsensitiveHashtable(); diff --git a/src/libraries/System.Collections.NonGeneric/tests/HashtableTests.cs b/src/libraries/System.Collections.NonGeneric/tests/HashtableTests.cs index c0fddb75701b64..e172e121feac85 100644 --- a/src/libraries/System.Collections.NonGeneric/tests/HashtableTests.cs +++ b/src/libraries/System.Collections.NonGeneric/tests/HashtableTests.cs @@ -779,7 +779,7 @@ public void Values_ModifyingHashtable_ModifiesCollection() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void HashCodeProvider_Set_ImpactsSearch() { var hash = new ComparableHashtable(CaseInsensitiveHashCodeProvider.DefaultInvariant, StringComparer.OrdinalIgnoreCase); @@ -834,7 +834,7 @@ public void HashCodeProvider_Comparer_IncompatibleGetSet_Throws() AssertExtensions.Throws(null, () => hash.Comparer = StringComparer.OrdinalIgnoreCase); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Comparer_Set_ImpactsSearch() { var hash = new ComparableHashtable(CaseInsensitiveHashCodeProvider.DefaultInvariant, StringComparer.OrdinalIgnoreCase); diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ConstructorTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ConstructorTests.cs index e34009b7c97fb2..9ede42f1d18d42 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ConstructorTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ConstructorTests.cs @@ -20,7 +20,7 @@ public void Constructor_Provider_Comparer() Assert.Equal(0, coll.Count); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Constructor_Int_Provider_Comparer() { #pragma warning disable CS0618 // Type or member is obsolete diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.CopyToTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.CopyToTests.cs index d8eb5ae5777d72..74b351ff4007ce 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.CopyToTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.CopyToTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameObjectCollectionBaseCopyToTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0, 0)] [InlineData(0, 5)] [InlineData(10, 0)] @@ -39,7 +39,7 @@ public void CopyTo(int count, int index) Assert.Equal(previousCount, copyArray.Length); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void CopyTo_Invalid(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetAllValuesTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetAllValuesTests.cs index b9a64ecc30aedb..941b943be7ab0f 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetAllValuesTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetAllValuesTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class GetAllValuesTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0, typeof(object))] [InlineData(0, typeof(Foo))] [InlineData(10, typeof(object))] @@ -33,7 +33,7 @@ private static void VerifyGetAllValues(NameObjectCollectionBase nameObjectCollec } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void GetAllValues_Invalid() { MyNameObjectCollection nameObjectCollection = new MyNameObjectCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetEnumeratorTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetEnumeratorTests.cs index 3f7ec1338bf876..7d1c06f066a113 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetEnumeratorTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.GetEnumeratorTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameObjectCollectionBaseGetEnumeratorTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void GetEnumerator(int count) @@ -29,7 +29,7 @@ public void GetEnumerator(int count) } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void GetEnumerator_Invalid(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.KeysTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.KeysTests.cs index db56a194ad67a1..f1edcf9aad34a3 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.KeysTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.KeysTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameObjectCollectionBaseKeysTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void Keys_PreservesInstance(int count) @@ -16,7 +16,7 @@ public void Keys_PreservesInstance(int count) Assert.Same(nameObjectCollection.Keys, nameObjectCollection.Keys); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void Keys_GetEnumerator(int count) @@ -40,7 +40,7 @@ public void Keys_GetEnumerator(int count) } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void Keys_GetEnumerator_Invalid(int count) @@ -85,7 +85,7 @@ public void Keys_GetEnumerator_Invalid(int count) Assert.Throws(() => enumerator.Reset()); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void Keys_Properties(int count) @@ -97,7 +97,7 @@ public void Keys_Properties(int count) Assert.False(keysCollection.IsSynchronized); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0, 0)] [InlineData(0, 5)] [InlineData(10, 0)] @@ -145,7 +145,7 @@ private static void Keys_CopyTo_Helper(MyNameObjectCollection nameObjectCollecti Assert.Equal(previousCount, keysArray.Length); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void Keys_CopyTo_Invalid(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ReadOnlyTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ReadOnlyTests.cs index e05a2e78936c5c..05af2efe640003 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ReadOnlyTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.ReadOnlyTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameObjectCollectionBaseReadOnlyTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void IsReadOnly_Set() { MyNameObjectCollection nameObjectCollection = Helpers.CreateNameObjectCollection(10); diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.RemoveAtTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.RemoveAtTests.cs index fe99de5af1b7da..4ec96a64fd9ea7 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.RemoveAtTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.RemoveAtTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameObjectCollectionBaseRemoveAtTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void RemoveAt() { MyNameObjectCollection nameObjectCollection = Helpers.CreateNameObjectCollection(10); @@ -59,7 +59,7 @@ public void RemoveAt() } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void RemoveAt_InvalidIndex_ThrowsArgumentOutOfRangeException(int count) @@ -69,7 +69,7 @@ public void RemoveAt_InvalidIndex_ThrowsArgumentOutOfRangeException(int count) AssertExtensions.Throws("index", () => nameObjectCollection.RemoveAt(count)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void RemoveAt_ReadOnly_ThrowsNotSupportedException() { MyNameObjectCollection nameObjectCollection = Helpers.CreateNameObjectCollection(1); diff --git a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.SetItemTests.cs b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.SetItemTests.cs index 398aab7fabf132..c1f1bd54acfcbf 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.SetItemTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameObjectCollectionBase/NameObjectCollectionBase.SetItemTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameObjectCollectionBaseSetItemTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Set_ObjectAtIndex_ModifiesCollection() { var noc = new MyNameObjectCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddNVCTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddNVCTests.cs index 82578fb7de13a0..538b9c924ce2a1 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddNVCTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddNVCTests.cs @@ -8,7 +8,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionAddNameValueCollectionTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0, 0)] [InlineData(0, 5)] [InlineData(5, 0)] @@ -43,7 +43,7 @@ public void Add(int count1, int count2) } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Add_ExistingKeys() { NameValueCollection nameValueCollection1 = new NameValueCollection(); @@ -61,7 +61,7 @@ public void Add_ExistingKeys() Assert.Equal(new string[] { value2, value1 }, nameValueCollection2.GetValues(name)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Add_MultipleValues() { NameValueCollection nameValueCollection1 = new NameValueCollection(); @@ -104,7 +104,7 @@ public void Add_NameValueCollection_WithNullKeys() Assert.Equal(nullKeyValue1 + "," + nullKeyValue2, nameValueCollection3[null]); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Add_NameValueCollection_WithNullValues() { NameValueCollection nameValueCollection1 = new NameValueCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddStringStringTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddStringStringTests.cs index 28ba9d7ad71ad0..dc148f198c3581 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddStringStringTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.AddStringStringTests.cs @@ -8,7 +8,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionAddStringStringTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Add() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -82,7 +82,7 @@ public void Add_NullName() Assert.False(nameValueCollection.HasKeys()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Add_NullValue() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -108,7 +108,7 @@ public void Add_NullValue() Assert.True(nameValueCollection.HasKeys()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Add_AddingValueToExistingName_AppendsValueToOriginalValue() { var nameValueCollection = new NameValueCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.ClearTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.ClearTests.cs index acdf6b442eabb4..b15395c354d111 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.ClearTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.ClearTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionClearTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(10)] public void Clear(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs index 409ccd25c8ab41..2daff0ba450f96 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionCopyToTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0, 0)] [InlineData(0, 1)] [InlineData(5, 0)] @@ -37,7 +37,7 @@ public void CopyTo(int count, int index) } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void CopyTo_MultipleValues_SameName() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -51,7 +51,7 @@ public void CopyTo_MultipleValues_SameName() Assert.Equal(nameValueCollection[0], dest[0]); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void CopyTo_Invalid(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CtorTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CtorTests.cs index c7106153faf89f..20cf0fe5af5123 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CtorTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CtorTests.cs @@ -46,7 +46,7 @@ public void Ctor_Int_Provider_Comparer() Assert.False(((ICollection)nameValueCollection).IsSynchronized); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Ctor_Int(int capacity) @@ -85,7 +85,7 @@ public static IEnumerable Ctor_NameValueCollection_TestData() yield return new object[] { Helpers.CreateNameValueCollection(10) }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(Ctor_NameValueCollection_TestData))] public void Ctor_NameValueCollection(NameValueCollection nameValueCollection1) { @@ -126,7 +126,7 @@ public static IEnumerable Ctor_Int_NameValueCollection_TestData() yield return new object[] { 15, Helpers.CreateNameValueCollection(10) }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(Ctor_Int_NameValueCollection_TestData))] public void Ctor_Int_NameValueCollection(int capacity, NameValueCollection nameValueCollection1) { @@ -156,7 +156,7 @@ public static IEnumerable Ctor_Int_IEqualityComparer_TestData() yield return new object[] { 10, null }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(Ctor_Int_IEqualityComparer_TestData))] public void Ctor_Int_IEqualityComparer(int capacity, IEqualityComparer equalityComparer) { @@ -170,7 +170,7 @@ public static IEnumerable Ctor_IEqualityComparer_TestData() yield return new object[] { null }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(Ctor_IEqualityComparer_TestData))] public void Ctor_IEqualityComparer(IEqualityComparer equalityComparer) { diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetIntTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetIntTests.cs index ba652ced4b86ba..c8b4fdc6c996e2 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetIntTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetIntTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionGetIntTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Get_InvalidIndex_ThrowsArgumentOutOfRangeException(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetItemTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetItemTests.cs index c6e723e1a52f5d..82e63d654e47c2 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetItemTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetItemTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionGetItemTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Item_Get_InvalidIndex_ThrowsArgumentOutOfRangeException(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetKeyTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetKeyTests.cs index 4e0b496448802b..c283053d0c70c9 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetKeyTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetKeyTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionGetKeyTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Get_InvalidIndex_ThrowsArgumentOutOfRangeException(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetStringTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetStringTests.cs index d61cabe1900b39..47c6e1db59e8d4 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetStringTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetStringTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionGetStringTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Get_NoSuchName_ReturnsNull(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesIntTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesIntTests.cs index b179a4b6bfd2c8..dc7bfaca4f64c2 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesIntTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesIntTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionGetValuesIntTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void GetValues_InvalidIndex_ThrowsArgumentOutOfRangeException(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesStringTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesStringTests.cs index b7deb3badfc452..0371dc9e20ad84 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesStringTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.GetValuesStringTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionGetValuesStringTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void GetValues_NoSuchName_ReturnsNull(int count) diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.RemoveTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.RemoveTests.cs index 65e582c346a642..34e48ae0ba55d8 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.RemoveTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.RemoveTests.cs @@ -8,7 +8,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionRemoveTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Remove(int count) @@ -46,7 +46,7 @@ public void Remove(int count) } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Remove_MultipleValues_SameName() { NameValueCollection nameValueCollection = new NameValueCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetItemTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetItemTests.cs index dc9e8bb14598be..de3e23ecd4086f 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetItemTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetItemTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionSetItemTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Item_Set() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -22,7 +22,7 @@ public void Item_Set() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Item_Set_OvewriteExistingValue() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -35,7 +35,7 @@ public void Item_Set_OvewriteExistingValue() Assert.Equal(new string[] { value }, nameValueCollection.GetValues(name)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Item_Set_NullName(int count) @@ -53,7 +53,7 @@ public void Item_Set_NullName(int count) Assert.Equal(newNullNameValue, nameValueCollection[null]); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Item_Set_NullValue(int count) @@ -74,7 +74,7 @@ public void Item_Set_NullValue(int count) Assert.Null(nameValueCollection[nullValueName]); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Item_Set_IsCaseSensitive() { NameValueCollection nameValueCollection = new NameValueCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetTests.cs b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetTests.cs index 79e8c037ed15bf..6873b3e8a22d24 100644 --- a/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.SetTests.cs @@ -7,7 +7,7 @@ namespace System.Collections.Specialized.Tests { public class NameValueCollectionSetTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Set() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -23,7 +23,7 @@ public void Set() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Set_OvewriteExistingValue() { NameValueCollection nameValueCollection = new NameValueCollection(); @@ -36,7 +36,7 @@ public void Set_OvewriteExistingValue() Assert.Equal(new string[] { value }, nameValueCollection.GetValues(name)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Set_NullName(int count) @@ -54,7 +54,7 @@ public void Set_NullName(int count) Assert.Equal(newNullNameValue, nameValueCollection.Get(null)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [InlineData(0)] [InlineData(5)] public void Set_NullValue(int count) @@ -75,7 +75,7 @@ public void Set_NullValue(int count) Assert.Null(nameValueCollection.Get(nullValueName)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Set_IsCaseSensitive() { NameValueCollection nameValueCollection = new NameValueCollection(); diff --git a/src/libraries/System.Collections.Specialized/tests/OrderedDictionary/OrderedDictionaryTests.cs b/src/libraries/System.Collections.Specialized/tests/OrderedDictionary/OrderedDictionaryTests.cs index f24d0cd7892e34..ac1163d28c0410 100644 --- a/src/libraries/System.Collections.Specialized/tests/OrderedDictionary/OrderedDictionaryTests.cs +++ b/src/libraries/System.Collections.Specialized/tests/OrderedDictionary/OrderedDictionaryTests.cs @@ -35,7 +35,7 @@ public void CreatingWithDifferentCapacityValues() } // public OrderedDictionary(IEqualityComparer comparer); - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void PassingEqualityComparers() { var d1 = new OrderedDictionary(StringComparer.InvariantCultureIgnoreCase); @@ -55,7 +55,7 @@ public void PassingEqualityComparers() } // public OrderedDictionary(int capacity, IEqualityComparer comparer); - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void PassingCapacityAndIEqualityComparer() { var d1 = new OrderedDictionary(-1000, StringComparer.InvariantCultureIgnoreCase); @@ -626,7 +626,7 @@ public void KeysAndValuesPropertiesNotSupportWritableIList(bool testKeysProperty Assert.Throws(() => list.RemoveAt(0)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void IListedKeysPropertyCanUseCustomEqualityComparer() { var orderedDictionary = new OrderedDictionary(StringComparer.InvariantCultureIgnoreCase); diff --git a/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs b/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs index 37297f382c0a36..d6809b65a09418 100644 --- a/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs +++ b/src/libraries/System.Collections/tests/Generic/Dictionary/HashCollisionScenarios/OutOfBoundsRegression.cs @@ -42,7 +42,7 @@ public static void OutOfBoundsRegression() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void ComparerImplementations_Dictionary_WithWellKnownStringComparers() { Type nonRandomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); @@ -118,7 +118,7 @@ static void RunDictionaryTest( } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void ComparerImplementations_HashSet_WithWellKnownStringComparers() { Type nonRandomizedOrdinalComparerType = typeof(object).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer+OrdinalComparer", throwOnError: true); @@ -336,7 +336,6 @@ private static void ValidateBehaviorOfInternalComparerVsPublicComparer(IEquality { publicComparer = EqualityComparer.Default; } - foreach (var pair in new[] { ("Hello", "Hello"), // exactly equal ("Hello", "Goodbye"), // not equal at all diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowComparerTest.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowComparerTest.cs index c3350234eacc76..0e8fb95a74e6df 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowComparerTest.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowComparerTest.cs @@ -45,7 +45,7 @@ public void Default() Assert.Same(c1, c2); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void EqualsTest() { DataRowComparer c = DataRowComparer.Default; @@ -123,7 +123,7 @@ public void EqualsTest() Assert.False(c.Equals(r1, r4), "#H2"); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Equals_Rows_Detached() { DataRowComparer c = DataRowComparer.Default; @@ -160,7 +160,7 @@ public void Equals_Rows_Detached() Assert.True(c.Equals(r2, r3), "#C3"); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Equals_Rows_Deleted() { DataRowComparer c = DataRowComparer.Default; diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowExtensionsTest.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowExtensionsTest.cs index 3cf12c4d4648d2..a03fc946672ebd 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowExtensionsTest.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataRowExtensionsTest.cs @@ -55,7 +55,7 @@ DataRow SetupRow() return row; } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Field_T_DBNullFieldValue() { DataRow row = SetupRow(); @@ -69,7 +69,7 @@ public void Field_T_DBNullFieldValue() Assert.Null(i); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Field_T_DBNullFieldValue_ValueType() { DataRow row = SetupRow(); diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataTableExtensionsTest.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataTableExtensionsTest.cs index edda9c89df4f95..7f6d7bb12c0f29 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataTableExtensionsTest.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/Mono/DataTableExtensionsTest.cs @@ -42,7 +42,7 @@ public class DataTableExtensionsTest { private string _testDataSet = "Mono/testdataset1.xml"; - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void CopyToDataTableNoArgNoRows() { DataTable dt = new DataTable(); @@ -53,7 +53,7 @@ public void CopyToDataTableNoArgNoRows() Assert.Throws(() => dt.AsEnumerable().CopyToDataTable()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void CopyToDataTableNoArg() { DataTable dt = new DataTable(); @@ -65,7 +65,7 @@ public void CopyToDataTableNoArg() Assert.Equal("foo", dst.Rows[0]["CName"]); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void CopyToDataTableTableArgNoRows() { DataTable dt = new DataTable(); diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowComparerTests.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowComparerTests.cs index 7652cfe9dac9dc..db2a3420cde136 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowComparerTests.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowComparerTests.cs @@ -278,7 +278,7 @@ public static IEnumerable Equals_TestData() }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(Equals_TestData))] public void Equals_Rows_ReturnsExpected(DataRow row1, DataRow row2, bool expected) { @@ -286,7 +286,7 @@ public void Equals_Rows_ReturnsExpected(DataRow row1, DataRow row2, bool expecte Assert.Equal(expected, DataRowComparer.Default.Equals(row2, row1)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Equals_NullStringValueInStringArray_CanBeCompared() { var table = new DataTable("Table"); @@ -307,7 +307,7 @@ public void Equals_NullStringValueInStringArray_CanBeCompared() Assert.False(DataRowComparer.Default.Equals(row3, row2)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Equals_DeletedRow_ThrowsInvalidOperationException() { var table = new DataTable("Table"); @@ -342,7 +342,7 @@ public static IEnumerable GetHashCode_TestData() yield return new object[] { multidimensionalArray, multidimensionalArray.GetHashCode() }; } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(GetHashCode_TestData))] public void GetHashCode_HasColumns_ReturnsExpected(object value, int expected) { @@ -362,7 +362,7 @@ public void GetHashCode_NoColumns_ReturnsZero() Assert.Equal(0, DataRowComparer.Default.GetHashCode(table.NewRow())); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void GetHashCode_OneColumn_DoesNotReturnZero() { var comparer = DataRowComparer.Default; @@ -379,7 +379,7 @@ public void GetHashCode_NullRow_ThrowsArgumentNullException() AssertExtensions.Throws("row", () => DataRowComparer.Default.GetHashCode(null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void GetHashCode_DeletedRow_ThrowsInvalidOperationException() { var table = new DataTable("Table"); diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowExtensionsTests.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowExtensionsTests.cs index e9acec3f8c4460..340eb77e7d60a2 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowExtensionsTests.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/DataRowExtensionsTests.cs @@ -93,7 +93,7 @@ public void Field_ColumnVersion_NullColumnThrows() AssertExtensions.Throws("column", () => DataRowExtensions.Field(row, column: null, version: DataRowVersion.Default)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Field_NonNullable_Enum() { DataTable table = new DataTable("test"); @@ -105,7 +105,7 @@ public void Field_NonNullable_Enum() Assert.Equal(SomeEnum.Foo, table.Rows[0].Field("col")); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Field_Nullable_Enum() { DataTable table = new DataTable("test"); @@ -136,7 +136,7 @@ public void SetField_IndexValue_NullColumnThrows() Assert.Throws(() => DataRowExtensions.SetField(row, columnIndex: -1, value: 0)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void SetField_IndexValue_NullValueReplacedByDBNull() { DataTable table = new DataTable("test"); @@ -161,7 +161,7 @@ public void SetField_NameValue_NullColumnNameThrows() AssertExtensions.Throws("name", () => DataRowExtensions.SetField(row, columnName: null, value: 0)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void SetField_NameValue_NullValueReplacedByDBNull() { DataTable table = new DataTable("test"); @@ -187,7 +187,7 @@ public void SetField_ColumnValue_NullColumnThrows() AssertExtensions.Throws("column", () => DataRowExtensions.SetField(row, column: null, value: 0)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void SetField_ColumnValue_NullValueReplacedByDBNull() { DataTable table = new DataTable("test"); diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/EnumerableRowCollectionExtensionsTests.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/EnumerableRowCollectionExtensionsTests.cs index 0806c4a586eb80..2c7a483cd2b663 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/EnumerableRowCollectionExtensionsTests.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/EnumerableRowCollectionExtensionsTests.cs @@ -33,7 +33,7 @@ public override int Compare(T x, T y) return (table, one, two, three); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Where_SuccessfullyFindRow() { TypedTableBase table = new TestTypedTable(); @@ -51,7 +51,7 @@ public void Where_SuccessfullyFindRow() Assert.Same(two, filtered.First()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void OrderBy_AddSortExpressionValidation() { var (table, one, two, three) = InstantiateTable(); @@ -65,7 +65,7 @@ public void OrderBy_AddSortExpressionValidation() Assert.Equal(new DataRow[] { zero, one, two, three }, compared); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void OrderByDescending_AddSortExpressionValidation() { var (table, one, two, three) = InstantiateTable(); @@ -79,7 +79,7 @@ public void OrderByDescending_AddSortExpressionValidation() Assert.Equal(new DataRow[] { four, three, two, one }, comparedBackwards); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ThenBy_AddSortExpressionValidation() { var (table, one, two, three) = InstantiateTable(); @@ -97,7 +97,7 @@ public void ThenBy_AddSortExpressionValidation() } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ThenByDescending_AddSortExpressionValidation() { var (table, one, two, three) = InstantiateTable(); diff --git a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/TypedTableBaseExtensionsTests.cs b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/TypedTableBaseExtensionsTests.cs index 02d2b2153d3d0d..ef8516dfc3e163 100644 --- a/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/TypedTableBaseExtensionsTests.cs +++ b/src/libraries/System.Data.Common/tests/System.Data.DataSetExtensions.Tests/System/Data/TypedTableBaseExtensionsTests.cs @@ -44,7 +44,7 @@ public class TestTypedTable : TypedTableBase where T : DataRow public TestTypedTable() : base() { } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ElementAtOrDefault_ValidIndex() { TypedTableBase table = new TestTypedTable(); @@ -54,7 +54,7 @@ public void ElementAtOrDefault_ValidIndex() Assert.Same(zero, table.ElementAtOrDefault(0)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ElementAtOrDefault_InvalidIndex() { TypedTableBase table = new TestTypedTable(); @@ -64,7 +64,7 @@ public void ElementAtOrDefault_InvalidIndex() Assert.Same(default(DataRow), table.ElementAtOrDefault(1)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Select_ToListOfInts() { TypedTableBase table = new TestTypedTable(); diff --git a/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs b/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs index 3c88b4a785bcc3..9d73511e9b07a9 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/DataTableExtensionsTest.cs @@ -51,7 +51,7 @@ public void AsDataView_NullSource_ThrowsArgumentNullException() AssertExtensions.Throws("source", () => DataTableExtensions.AsDataView(null)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void AsDataView_Source_Succeeds() { DataView dv = _dt.AsEnumerable().Where(r => r.Field("alias").Length > 6).AsDataView(); diff --git a/src/libraries/System.Net.Mail/tests/Functional/MailAddressTest.cs b/src/libraries/System.Net.Mail/tests/Functional/MailAddressTest.cs index 70c30ee55a86a3..019ceee0d41b2b 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/MailAddressTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/MailAddressTest.cs @@ -210,7 +210,7 @@ public void EqualsTest2() Assert.Equal(n, n2); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void GetHashCodeTest() { var n = new MailAddress("Mr. Bar "); @@ -218,7 +218,7 @@ public void GetHashCodeTest() Assert.Equal(n.GetHashCode(), n2.GetHashCode()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void GetHashCodeTest2() { var n = new MailAddress("Mr. Bar "); diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 68969b6453939b..ac57b0760021b2 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4100,6 +4100,9 @@ {0} is not supported when HybridGlobalization=true. Disable it to load larger ICU bundle, then use this option. + + {0} is not supported for {1}, when HybridGlobalization=true. Disable it to load larger ICU bundle, then use this option. + The body of this method was removed by the AOT compiler because it's not callable. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs index f2c7dd254ae2ff..75440b2a23c458 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs @@ -692,6 +692,16 @@ private unsafe SortKey IcuCreateSortKey(string source, CompareOptions options) Debug.Assert(!GlobalizationMode.Invariant); Debug.Assert(!GlobalizationMode.UseNls); +#if TARGET_BROWSER + // JS cannot create locale-sensitive sort key, use invaraint functions instead. + if (GlobalizationMode.Hybrid) + { + if (!_isInvariantCulture) + throw new PlatformNotSupportedException(GetPNSEWithReason("CreateSortKey", "non-invariant culture")); + return InvariantCreateSortKey(source, options); + } +#endif + if ((options & ValidCompareMaskOffFlags) != 0) { throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); @@ -743,6 +753,15 @@ private unsafe int IcuGetSortKey(ReadOnlySpan source, Span destinati Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert((options & ValidCompareMaskOffFlags) == 0); +#if TARGET_BROWSER + if (GlobalizationMode.Hybrid) + { + if (!_isInvariantCulture) + throw new PlatformNotSupportedException(GetPNSEWithReason("GetSortKey", "non-invariant culture")); + return InvariantGetSortKey(source, destination, options); + } +#endif + // It's ok to pass nullptr (for empty buffers) to ICU's sort key routines. int actualSortKeyLength; @@ -785,6 +804,15 @@ private unsafe int IcuGetSortKeyLength(ReadOnlySpan source, CompareOptions Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert((options & ValidCompareMaskOffFlags) == 0); +#if TARGET_BROWSER + if (GlobalizationMode.Hybrid) + { + if (!_isInvariantCulture) + throw new PlatformNotSupportedException(GetPNSEWithReason("GetSortKeyLength", "non-invariant culture")); + return InvariantGetSortKeyLength(source, options); + } +#endif + // It's ok to pass nullptr (for empty buffers) to ICU's sort key routines. fixed (char* pSource = &MemoryMarshal.GetReference(source)) @@ -833,6 +861,20 @@ private unsafe int IcuGetHashCodeOfString(ReadOnlySpan source, CompareOpti Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); +#if TARGET_BROWSER + if (GlobalizationMode.Hybrid) + { + if (!_isInvariantCulture && !LocalizedHashCodeSupportsCompareOptions(options)) + { + throw new PlatformNotSupportedException(GetPNSEWithReason("GetHashCode", "non-invariant culture with CompareOptions different than None or IgnoreCase")); + } + + // JS cannot create locale-sensitive HashCode, use invaraint functions instead + ReadOnlySpan sanitizedSource = SanitizeForInvariantHash(source, options); + return InvariantGetHashCode(sanitizedSource, options); + } +#endif + // according to ICU User Guide the performance of ucol_getSortKey is worse when it is called with null output buffer // the solution is to try to fill the sort key in a temporary buffer of size equal 4 x string length // (The ArrayPool used to have a limit on the length of buffers it would cache; this code was avoiding diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs index 739f461cc8628b..a26ba62928bcd2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Invariant.cs @@ -126,5 +126,15 @@ private static int InvariantGetSortKeyLength(ReadOnlySpan source, CompareO return byteLength; } + + private static int InvariantGetHashCode(ReadOnlySpan source, CompareOptions options) + { + if ((options & CompareOptions.IgnoreCase) == 0) + { + return string.GetHashCode(source); + } + + return string.GetHashCodeOrdinalIgnoreCase(source); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.WebAssembly.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.WebAssembly.cs index dab9c1aac0ba70..1d265dc70450a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.WebAssembly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.WebAssembly.cs @@ -8,6 +8,16 @@ namespace System.Globalization { public partial class CompareInfo { + // invariant culture has empty CultureInfo.ToString() and + // m_name == CultureInfo._name == CultureInfo.ToString() + private bool _isInvariantCulture => string.IsNullOrEmpty(m_name); + + private TextInfo? _thisTextInfo; + + private TextInfo thisTextInfo => _thisTextInfo ??= new CultureInfo(m_name).TextInfo; + + private static bool LocalizedHashCodeSupportsCompareOptions(CompareOptions options) => + options == CompareOptions.IgnoreCase || options == CompareOptions.None; private static void AssertHybridOnWasm(CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); @@ -119,6 +129,63 @@ private unsafe int JsIndexOfCore(ReadOnlySpan source, ReadOnlySpan t return idx; } + // there are chars that are ignored by ICU hashing algorithm but not ignored by invariant hashing + // Control: 1105 (out of 1105) + // Format: 697 (out of 731) + // OtherPunctuation: 6919 (out of 7004) + // SpaceSeparator: 289 (out of 289) + // OpenPunctuation: 1275 (out of 1343) + // ClosePunctuation: 1241 (out of 1309) + // DashPunctuation: 408 (out of 425) + // ConnectorPunctuation: 170 (out of 170) + // InitialQuotePunctuation: 204 (out of 204) + // FinalQuotePunctuation: 170 (out of 170) + // LineSeparator: 17 (out of 17) + // ParagraphSeparator: 17 (out of 17) + // OtherLetter: 34 (out of 784142) + // SpacingCombiningMark: 68 (out of 4420) + // ModifierLetter: 51 (out of 4012) + // EnclosingMark: 85 (out of 221) + // NonSpacingMark: 3281 (out of 18105) + // we can skip them all (~1027k chars) by checking for the remaining UnicodeCategories (~291k chars) + // skipping more characters than ICU would lead to hashes with smaller distribution and more collisions in hash tables + // but it makes the behavior correct and consistent with locale-aware equals, which is acceptable tradeoff + private static bool ShouldNotBeSkipped(UnicodeCategory category) => + category == UnicodeCategory.LowercaseLetter || + category == UnicodeCategory.UppercaseLetter || + category == UnicodeCategory.TitlecaseLetter || + category == UnicodeCategory.LetterNumber || + category == UnicodeCategory.OtherNumber || + category == UnicodeCategory.Surrogate || + category == UnicodeCategory.PrivateUse || + category == UnicodeCategory.MathSymbol || + category == UnicodeCategory.CurrencySymbol || + category == UnicodeCategory.ModifierSymbol || + category == UnicodeCategory.OtherSymbol || + category == UnicodeCategory.OtherNotAssigned; + + private ReadOnlySpan SanitizeForInvariantHash(ReadOnlySpan source, CompareOptions options) + { + char[] result = new char[source.Length]; + int resultIndex = 0; + foreach (char c in source) + { + UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c); + if (ShouldNotBeSkipped(category)) + { + result[resultIndex++] = c; + } + } + if ((options & CompareOptions.IgnoreCase) != 0) + { + string resultStr = new string(result, 0, resultIndex); + // JS-based ToUpper, to keep cases like Turkish I working + resultStr = thisTextInfo.ToUpper(resultStr); + return resultStr.AsSpan(); + } + return result.AsSpan(0, resultIndex); + } + private static bool IndexingOptionsNotSupported(CompareOptions options) => (options & CompareOptions.IgnoreSymbols) == CompareOptions.IgnoreSymbols; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs index 7d46be4685e0dc..729fe7d4383b5e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs @@ -1450,10 +1450,6 @@ public SortKey GetSortKey(string source) private SortKey CreateSortKeyCore(string source, CompareOptions options) => GlobalizationMode.UseNls ? NlsCreateSortKey(source, options) : -#if TARGET_BROWSER - GlobalizationMode.Hybrid ? - throw new PlatformNotSupportedException(GetPNSEText("SortKey")) : -#endif IcuCreateSortKey(source, options); /// @@ -1493,10 +1489,6 @@ public int GetSortKey(ReadOnlySpan source, Span destination, Compare private int GetSortKeyCore(ReadOnlySpan source, Span destination, CompareOptions options) => GlobalizationMode.UseNls ? NlsGetSortKey(source, destination, options) : -#if TARGET_BROWSER - GlobalizationMode.Hybrid ? - throw new PlatformNotSupportedException(GetPNSEText("SortKey")) : -#endif IcuGetSortKey(source, destination, options); /// @@ -1530,10 +1522,6 @@ public int GetSortKeyLength(ReadOnlySpan source, CompareOptions options = private int GetSortKeyLengthCore(ReadOnlySpan source, CompareOptions options) => GlobalizationMode.UseNls ? NlsGetSortKeyLength(source, options) : -#if TARGET_BROWSER - GlobalizationMode.Hybrid ? - throw new PlatformNotSupportedException(GetPNSEText("SortKey")) : -#endif IcuGetSortKeyLength(source, options); public override bool Equals([NotNullWhen(true)] object? value) @@ -1576,12 +1564,7 @@ public int GetHashCode(ReadOnlySpan source, CompareOptions options) return GetHashCodeOfStringCore(source, options); } - if ((options & CompareOptions.IgnoreCase) == 0) - { - return string.GetHashCode(source); - } - - return string.GetHashCodeOrdinalIgnoreCase(source); + return InvariantGetHashCode(source, options); } else { @@ -1607,10 +1590,6 @@ public int GetHashCode(ReadOnlySpan source, CompareOptions options) private unsafe int GetHashCodeOfStringCore(ReadOnlySpan source, CompareOptions options) => GlobalizationMode.UseNls ? NlsGetHashCodeOfString(source, options) : -#if TARGET_BROWSER - GlobalizationMode.Hybrid ? - throw new PlatformNotSupportedException(GetPNSEText("HashCode")) : -#endif IcuGetHashCodeOfString(source, options); public override string ToString() => "CompareInfo - " + Name; @@ -1649,6 +1628,7 @@ public SortVersion Version #if TARGET_BROWSER || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS private static string GetPNSEText(string funcName) => SR.Format(SR.PlatformNotSupported_HybridGlobalization, funcName); + private static string GetPNSEWithReason(string funcName, string reason) => SR.Format(SR.PlatformNotSupportedWithReason_HybridGlobalization, funcName, reason); #endif } } diff --git a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs index 293d959a01674d..5550b59c4d88ef 100644 --- a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs +++ b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs @@ -70,8 +70,6 @@ public static bool SupportsPhysicalMemoryMonitor } public static bool DoesNotSupportPhysicalMemoryMonitor => !SupportsPhysicalMemoryMonitor; - public static bool IsNotHybridGlobalizationOnBrowser => PlatformDetection.IsNotHybridGlobalizationOnBrowser; - private bool IsFullFramework = RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); private PokerMemoryCache CreatePokerMemoryCache(string name, string throwOnDisposed) @@ -86,7 +84,7 @@ private PokerMemoryCache CreatePokerMemoryCache(string name, string throwOnDispo return new PokerMemoryCache("MyCache", config); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ConstructorParameters() { MemoryCache mc; @@ -238,7 +236,7 @@ public void DefaultInstanceDefaults() mc.Trim(0); } - [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor), nameof(IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ConstructorValues() { var config = new NameValueCollection(); @@ -260,10 +258,7 @@ public void ConstructorValues() Assert.Equal(TimeSpan.FromMinutes(70), mc.PollingInterval); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [InlineData("true")] - [InlineData("false")] - [InlineData(null)] + [Theory, InlineData("true"), InlineData("false"), InlineData(null)] public void Indexer(string throwOnDisposed) { var mc = CreatePokerMemoryCache("MyCache", throwOnDisposed); @@ -308,10 +303,7 @@ public void Indexer(string throwOnDisposed) } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [InlineData("true")] - [InlineData("false")] - [InlineData(null)] + [Theory, InlineData("true"), InlineData("false"), InlineData(null)] //[ActiveIssue("https://github.com/dotnet/runtime/issues/1429")] public void Contains(string throwOnDisposed) { @@ -409,10 +401,7 @@ public void CreateCacheEntryChangeMonitor() Assert.True (monitor.HasChanged); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [InlineData("true")] - [InlineData("false")] - [InlineData(null)] + [Theory, InlineData("true"), InlineData("false"), InlineData(null)] public void AddOrGetExisting_String_Object_DateTimeOffset_String(string throwOnDisposed) { var mc = CreatePokerMemoryCache("MyCache", throwOnDisposed); @@ -686,10 +675,7 @@ public void AddOrGetExisting_CacheItem_CacheItemPolicy() Assert.Equal("AddOrGetExisting (CacheItem item, CacheItemPolicy policy)", mc.Calls[0]); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [InlineData("true")] - [InlineData("false")] - [InlineData(null)] + [Theory, InlineData("true"), InlineData("false"), InlineData(null)] public void Set_String_Object_CacheItemPolicy_String(string throwOnDisposed) { var mc = CreatePokerMemoryCache("MyCache", throwOnDisposed); @@ -915,10 +901,7 @@ public void Set_CacheItem_CacheItemPolicy() Assert.Equal("Set (string key, object value, CacheItemPolicy policy, string regionName = null)", mc.Calls[1]); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [InlineData("true")] - [InlineData("false")] - [InlineData(null)] + [Theory, InlineData("true"), InlineData("false"), InlineData(null)] public void Remove(string throwOnDisposed) { var mc = CreatePokerMemoryCache("MyCache", throwOnDisposed); @@ -1027,10 +1010,7 @@ public void Remove(string throwOnDisposed) } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] - [InlineData("true")] - [InlineData("false")] - [InlineData(null)] + [Theory, InlineData("true"), InlineData("false"), InlineData(null)] public void GetValues(string throwOnDisposed) { var mc = CreatePokerMemoryCache("MyCache", throwOnDisposed); @@ -1131,7 +1111,7 @@ public void ChangeMonitors() // Due to internal implementation details Trim has very few easily verifiable scenarios // ActiveIssue: https://github.com/dotnet/runtime/issues/36488 - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArm64Process))] [InlineData("true"), InlineData("false"), InlineData(null)] public void Trim(string throwOnDisposed) { @@ -1182,7 +1162,7 @@ public void Trim(string throwOnDisposed) } } - [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor), nameof(IsNotHybridGlobalizationOnBrowser))] + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] public void TestExpiredGetValues() { var config = new NameValueCollection(); @@ -1528,9 +1508,8 @@ public async Task GetCacheItem() public class MemoryCacheTestExpires4 { public static bool SupportsPhysicalMemoryMonitor => MemoryCacheTest.SupportsPhysicalMemoryMonitor; - public static bool IsNotHybridGlobalizationOnBrowser => MemoryCacheTest.IsNotHybridGlobalizationOnBrowser; - [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor), nameof(IsNotHybridGlobalizationOnBrowser))] + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] [SkipOnPlatform(TestPlatforms.LinuxBionic, "https://github.com/dotnet/runtime/issues/93106")] public async Task TestCacheShrink() { @@ -1589,9 +1568,8 @@ public async Task TestCacheShrink() public class MemoryCacheTestExpires5 { public static bool SupportsPhysicalMemoryMonitor => MemoryCacheTest.SupportsPhysicalMemoryMonitor; - public static bool IsNotHybridGlobalizationOnBrowser => MemoryCacheTest.IsNotHybridGlobalizationOnBrowser; - [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor), nameof(IsNotHybridGlobalizationOnBrowser))] + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] [SkipOnPlatform(TestPlatforms.LinuxBionic, "https://github.com/dotnet/runtime/issues/93106")] public async Task TestCacheExpiryOrdering() { diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs index bab4eaadd8b507..3046f50a98c3e9 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/GetStringComparerTests.cs @@ -19,7 +19,7 @@ public void GetStringComparer_Invalid() AssertExtensions.Throws("options", () => new CultureInfo("tr-TR").CompareInfo.GetStringComparer(CompareOptions.OrdinalIgnoreCase | CompareOptions.IgnoreCase)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] [InlineData("hello", "hello", "fr-FR", CompareOptions.IgnoreCase, 0, 0)] [InlineData("hello", "HELLo", "fr-FR", CompareOptions.IgnoreCase, 0, 0)] diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs new file mode 100644 index 00000000000000..a2247b39f636ec --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs @@ -0,0 +1,139 @@ +// 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; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text; +using Xunit; + +namespace System.Globalization.Tests +{ + public class CompareInfoHashCodeTests : CompareInfoTestsBase + { + public class CustomComparer : StringComparer + { + private readonly CompareInfo _compareInfo; + private readonly CompareOptions _compareOptions; + + public CustomComparer(CompareInfo cmpInfo, CompareOptions cmpOptions) + { + _compareInfo = cmpInfo; + _compareOptions = cmpOptions; + } + + public override int Compare(string x, string y) => + _compareInfo.Compare(x, y, _compareOptions); + + public override bool Equals(string x, string y) => + _compareInfo.Compare(x, y, _compareOptions) == 0; + + public override int GetHashCode(string obj) + { + return _compareInfo.GetHashCode(obj, _compareOptions); + } + } + + public static IEnumerable HashCodeLocalized_TestData() + { + yield return new object[] { s_invariantCompare, "foo", "Foo", CompareOptions.IgnoreCase }; + yield return new object[] { s_invariantCompare, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase }; // FAILS + yield return new object[] { s_invariantCompare, "igloo", "IGLOO", CompareOptions.IgnoreCase }; + yield return new object[] { new CultureInfo("pl-PL").CompareInfo, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase }; // FAILS + yield return new object[] { new CultureInfo("pl-PL").CompareInfo, "igloo", "IGLOO", CompareOptions.IgnoreCase }; + yield return new object[] { new CultureInfo("tr-TR").CompareInfo, "igloo", "\u0130GLOO", CompareOptions.IgnoreCase }; + yield return new object[] { new CultureInfo("tr-TR").CompareInfo, "igloo", "IGLOO", CompareOptions.IgnoreCase }; // FAILS + + if (!PlatformDetection.IsHybridGlobalizationOnBrowser) + { + // ActiveIssue: https://github.com/dotnet/runtime/issues/96400 + yield return new object[] { new CultureInfo("en-GB").CompareInfo, "100", "100!", CompareOptions.IgnoreSymbols }; // HG: equal: True, hashCodesEqual: False + yield return new object[] { new CultureInfo("ja-JP").CompareInfo, "\u30A2", "\u3042", CompareOptions.IgnoreKanaType }; // HG: equal: True, hashCodesEqual: False + yield return new object[] { new CultureInfo("en-GB").CompareInfo, "caf\u00E9", "cafe\u0301", CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType }; // HG: equal: True, hashCodesEqual: False + yield return new object[] { new CultureInfo("en-GB").CompareInfo, "caf\u00E9", "cafe\u0301", CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType }; // HG: equal: True, hashCodesEqual: False + } + } + + [Theory] + [MemberData(nameof(HashCodeLocalized_TestData))] + public void HashCodeLocalized(CompareInfo cmpInfo, string str1, string str2, CompareOptions options) + { + bool areEqual = cmpInfo.Compare(str1, str2, options) == 0; + var hashCode1 = cmpInfo.GetHashCode(str1, options); + var hashCode2 = cmpInfo.GetHashCode(str2, options); + bool areHashCodesEqual = hashCode1 == hashCode2; + + if (areEqual) + { + Assert.True(areHashCodesEqual); + } + else + { + Assert.False(areHashCodesEqual); + } + + // implication of the above behavior: + StringComparer stringComparer = new CustomComparer(cmpInfo, options); + TryAddToCustomDictionary(stringComparer, str1, str2, areHashCodesEqual); + } + + private void TryAddToCustomDictionary(StringComparer comparer, string str1, string str2, bool shouldFail) + { + Dictionary customDictionary = new Dictionary(comparer); + customDictionary.Add(str1, 0); + try + { + customDictionary.Add(str2, 1); + Assert.False(shouldFail); + } + catch (ArgumentException ex) + { + Assert.True(shouldFail); + Assert.Contains("An item with the same key has already been added.", ex.Message); + } + catch (Exception ex) + { + Assert.Fail($"Unexpected exception thrown: {ex}"); + } + } + + public static IEnumerable CheckHashingOfSkippedChars_TestData() + { + // one char from each ignored category that is skipped on ICU + yield return new object[] { '\u0008', s_invariantCompare }; // Control: BACKSPACE + yield return new object[] { '\u200B', s_invariantCompare }; // Format: ZERO WIDTH SPACE + yield return new object[] { '\u180A', s_invariantCompare }; // OtherPunctuation: MONGOLIAN NIRUGU + yield return new object[] { '\uFE73', s_invariantCompare }; // OtherLetter: THAI CHARACTER PAIYANNOI + yield return new object[] { '\u0F3E', s_invariantCompare }; // SpacingCombiningMark: "TIBETAN MARK GTER YIG MGO UM RTAGS GNYIS + yield return new object[] { '\u0640', s_invariantCompare }; // ModifierLetter: ARABIC TATWEEL + yield return new object[] { '\u0488', s_invariantCompare }; // EnclosingMark: COMBINING CYRILLIC HUNDRED THOUSANDS SIGN + yield return new object[] { '\u034F', s_invariantCompare }; // NonSpacingMark: DIAERESIS + CompareInfo thaiCmpInfo = new CultureInfo("th-TH").CompareInfo; + yield return new object[] { '\u0020', thaiCmpInfo }; // SpaceSeparator: SPACE + yield return new object[] { '\u0028', thaiCmpInfo }; // OpenPunctuation: LEFT PARENTHESIS + yield return new object[] { '\u007D', thaiCmpInfo }; // ClosePunctuation: RIGHT PARENTHESIS + yield return new object[] { '\u2013', thaiCmpInfo }; // DashPunctuation: EN DASH + yield return new object[] { '\u005F', thaiCmpInfo }; // ConnectorPunctuation: LOW LINE + yield return new object[] { '\u2018', thaiCmpInfo }; // InitialQuotePunctuation: LEFT SINGLE QUOTATION MARK + yield return new object[] { '\u2019', thaiCmpInfo }; // FinalQuotePunctuation: RIGHT SINGLE QUOTATION MARK + yield return new object[] { '\u2028', thaiCmpInfo }; // LineSeparator: LINE SEPARATOR + yield return new object[] { '\u2029', thaiCmpInfo }; // ParagraphSeparator: PARAGRAPH SEPARATOR + } + + [Theory] + [MemberData(nameof(CheckHashingOfSkippedChars_TestData))] + public void CheckHashingOfSkippedChars(char character, CompareInfo cmpInfo) + { + string str1 = $"a{character}b"; + string str2 = "ab"; + CompareOptions options = CompareOptions.None; + var hashCode1 = cmpInfo.GetHashCode(str1, options); + var hashCode2 = cmpInfo.GetHashCode(str2, options); + bool areHashCodesEqual = hashCode1 == hashCode2; + Assert.True(areHashCodesEqual); + StringComparer stringComparer = new CustomComparer(cmpInfo, options); + TryAddToCustomDictionary(stringComparer, str1, str2, areHashCodesEqual); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs index 596b0f3435ad1f..a0542ac5e13c7c 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.cs @@ -355,42 +355,6 @@ public void SortKeyKanaTest(CompareInfo compareInfo, string string1, string stri SortKeyTest(compareInfo, string1, string2, options, expected); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] - public void SortKeyTestNotSupported() - { - try - { - s_invariantCompare.GetSortKey(""); - AssertNotReached(); - } - catch(PlatformNotSupportedException pnse) - { - Assert.Equal(GetPNSEText("SortKey"), pnse.Message); - } - try - { - s_invariantCompare.GetSortKeyLength(ReadOnlySpan.Empty); - AssertNotReached(); - } - catch(PlatformNotSupportedException pnse) - { - Assert.Equal(GetPNSEText("SortKey"), pnse.Message); - } - - try - { - s_invariantCompare.GetHashCode("", CompareOptions.None); - AssertNotReached(); - } - catch(PlatformNotSupportedException pnse) - { - Assert.Equal(GetPNSEText("HashCode"), pnse.Message); - } - - string GetPNSEText(string funcName) => $"{funcName} is not supported when HybridGlobalization=true. Disable it to load larger ICU bundle, then use this option."; - void AssertNotReached() => Assert.Fail(); - } - [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int CompareStringEx(string lpLocaleName, uint dwCmpFlags, string lpString1, int cchCount1, string lpString2, int cchCount2, IntPtr lpVersionInformation, IntPtr lpReserved, int lParam); private const int NORM_LINGUISTIC_CASING = 0x08000000; // use linguistic rules for casing diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System.Globalization.Tests.csproj b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System.Globalization.Tests.csproj index e5ba39e62b7a70..6319571112d964 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System.Globalization.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System.Globalization.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs index 2b8c983fc83cd3..bb69f0df97a3ec 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/StringComparer.cs @@ -87,14 +87,15 @@ public static IEnumerable UpperLowerCasing_TestData() yield return new object[] { "abcd", "ABCD", "en-US" }; yield return new object[] { "latin i", "LATIN I", "en-US" }; - if (PlatformDetection.IsNotInvariantGlobalization && !PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic) + // https://github.com/dotnet/runtime/issues/95503 + if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnBrowser && !PlatformDetection.IsAndroid && !PlatformDetection.IsLinuxBionic) { yield return new object[] { "turky \u0131", "TURKY I", "tr-TR" }; yield return new object[] { "turky i", "TURKY \u0130", "tr-TR" }; } } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(UpperLowerCasing_TestData))] public static void CreateWithCulturesTest(string lowerForm, string upperForm, string cultureName) { @@ -112,7 +113,7 @@ public static void CreateWithCulturesTest(string lowerForm, string upperForm, st Assert.Equal(sc.GetHashCode((object) lowerForm), sc.GetHashCode((object) upperForm)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void InvariantTest() { Assert.True(StringComparer.InvariantCulture.Equals("test", "test"), "Same casing strings with StringComparer.InvariantCulture should be equal"); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs index b6160f82fcea42..3027c870855b5d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs @@ -16,7 +16,7 @@ public void Create_InvalidArguments_Throws() AssertExtensions.Throws("culture", () => StringComparer.Create(null, ignoreCase: true)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void Create_CreatesValidComparer() { StringComparer c = StringComparer.Create(CultureInfo.InvariantCulture, ignoreCase: true); @@ -117,7 +117,7 @@ public void CreateCultureOptions_InvalidArguments_Throws() Assert.Throws(() => StringComparer.Create(null, CompareOptions.None)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void CreateCultureOptions_CreatesValidComparer() { StringComparer c = StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs index b31f71b1e3b48c..c3396851055091 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringGetHashCodeTests.cs @@ -61,7 +61,7 @@ public static IEnumerable GetHashCode_TestData() () => { return CultureInfo.CurrentCulture.CompareInfo.GetHashCode("abc", CompareOptions.OrdinalIgnoreCase); } }; - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(GetHashCodeOrdinalIgnoreCase_TestData))] public void GetHashCode_OrdinalIgnoreCase_ReturnsSameHashCodeAsUpperCaseOrdinal(string input) { @@ -89,7 +89,8 @@ public static IEnumerable GetHashCodeOrdinalIgnoreCase_TestData() { yield return new object[] { "AaBbCcDdEeFfGgHh".Insert(i, "\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */) }; yield return new object[] { "AaBbCcDdEeFfGgHh".Insert(i, "\u044D" /* CYRILLIC SMALL LETTER E */) }; - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) + // https://github.com/dotnet/runtime/issues/95503 + if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform && PlatformDetection.IsNotHybridGlobalizationOnBrowser) yield return new object[] { "AaBbCcDdEeFfGgHh".Insert(i, "\u0131" /* LATIN SMALL LETTER DOTLESS I */) }; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs index d732e1644bb296..dbd6bf7cbee5b9 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs @@ -912,7 +912,7 @@ public static void GetHashCode_OfSpan_MatchesOfString() Assert.NotEqual("abc".GetHashCode(), string.GetHashCode("ABC".AsSpan())); // case differences } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] public static void GetHashCode_CompareInfo() { // ordinal @@ -932,7 +932,7 @@ public static void GetHashCode_CompareInfo() Assert.Equal("aeiXXabc".GetHashCode(StringComparison.InvariantCultureIgnoreCase), CultureInfo.InvariantCulture.CompareInfo.GetHashCode("aeiXXabc", CompareOptions.IgnoreCase)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public static void GetHashCode_CompareInfo_OfSpan() { // ordinal @@ -954,7 +954,7 @@ public static void GetHashCode_CompareInfo_OfSpan() public static IEnumerable GetHashCode_StringComparison_Data => StringComparisons.Select(value => new object[] { value }); - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Theory] [MemberData(nameof(GetHashCode_StringComparison_Data))] public static void GetHashCode_StringComparison(StringComparison comparisonType) { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs index bb500cea493b6d..f7e20fbfe89384 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs @@ -57,7 +57,8 @@ public static void Casing_Invariant(int original, int upper, int lower) Assert.Equal(new Rune(lower), Rune.ToLowerInvariant(rune)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybridOnBrowser))] + // HybridGlobalization on Browser uses Invariant HashCode and SortKey, so its effect does not match this of ICU [InlineData('0', '0', '0')] [InlineData('a', 'A', 'a')] [InlineData('i', 'I', 'i')] diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs index d7459be339a967..0947ccbdfc8954 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Specialized.Write.cs @@ -9,7 +9,7 @@ namespace System.Text.Json.Serialization.Tests { public abstract partial class CollectionTests { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public async Task Write_SpecializedCollection() { Assert.Equal(@"{""Data"":4}", await Serializer.SerializeWrapper(new BitVector32(4))); From 62d33ee48d57feba67b261b55db666bdc202b1c1 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Fri, 19 Jan 2024 13:33:10 -0800 Subject: [PATCH 139/189] Implement remaining unimplemented APIs for Builder types (#96805) * Implement 'DefinPInvokeMethod', save required/optional CustomModifiers, fix bugs found * Add global method, get method impl and tests * Implement DefineInitializedData(...) and UninitializedData(...), refactor field/method/type token logic because of global members * Update src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs Co-authored-by: Aaron Robinson * Load assemblies in unloadable context Co-authored-by: Steve Harter * Add test for DefineUninitializedData(...) * Set parameter count to 0 for RtFieldInfo * Throw when member token is not populated when Module.Get***MetadataToken methods called * Retrieving standalone signature not supported on mono * Create byte array with the size directly instead of null check --------- Co-authored-by: Aaron Robinson Co-authored-by: Steve Harter --- .../Reflection/Emit/RuntimeModuleBuilder.cs | 2 +- .../src/System/Reflection/RtFieldInfo.cs | 4 +- .../src/Resources/Strings.resx | 21 + .../Reflection/Emit/AssemblyBuilderImpl.cs | 12 +- .../Reflection/Emit/ConstructorBuilderImpl.cs | 9 +- .../Reflection/Emit/FieldBuilderImpl.cs | 22 +- .../Emit/GenericTypeParameterBuilderImpl.cs | 4 +- .../System/Reflection/Emit/ILGeneratorImpl.cs | 18 +- .../Reflection/Emit/MethodBuilderImpl.cs | 62 ++- .../Reflection/Emit/ModuleBuilderImpl.cs | 309 +++++++++---- .../Reflection/Emit/PropertyBuilderImpl.cs | 11 +- .../Emit/PseudoCustomAttributesData.cs | 51 ++- .../System/Reflection/Emit/SignatureHelper.cs | 59 ++- .../System/Reflection/Emit/TypeBuilderImpl.cs | 147 +++++- .../AssemblySaveAssemblyBuilder.cs | 33 +- .../AssemblySaveConstructorBuilderTests.cs | 1 - .../AssemblySaveEnumBuilderTests.cs | 2 +- .../AssemblySaveILGeneratorTests.cs | 432 +++++++++--------- .../AssemblySaveModuleBuilderTests.cs | 291 ++++++++++++ .../AssemblySaveTools.cs | 14 + ...cs => AssemblySaveTypeBuilderAPIsTests.cs} | 329 ++++++++++++- .../AssemblySaveTypeBuilderTests.cs | 10 +- .../tests/System.Reflection.Emit.Tests.csproj | 3 +- 23 files changed, 1405 insertions(+), 441 deletions(-) create mode 100644 src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs rename src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/{AssemblySaveMethodBuilderTests.cs => AssemblySaveTypeBuilderAPIsTests.cs} (59%) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs index 667bd0d54a022d..c78d8da5a9a07f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeModuleBuilder.cs @@ -829,7 +829,7 @@ protected override void CreateGlobalFunctionsCore() if (_hasGlobalBeenCreated) { // cannot create globals twice - throw new InvalidOperationException(SR.InvalidOperation_NotADebugModule); + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); } _globalTypeBuilder.CreateType(); _hasGlobalBeenCreated = true; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs index 15ce2971bed747..6a8aaf6898b834 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs @@ -256,12 +256,12 @@ private RuntimeType InitializeFieldType() public override Type[] GetRequiredCustomModifiers() { - return GetSignature().GetCustomModifiers(1, true); + return GetSignature().GetCustomModifiers(0, true); } public override Type[] GetOptionalCustomModifiers() { - return GetSignature().GetCustomModifiers(1, false); + return GetSignature().GetCustomModifiers(0, false); } internal Signature GetSignature() => new Signature(this, m_declaringType); diff --git a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx index 84f91988b3ba99..7a27f2807a39e6 100644 --- a/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Emit/src/Resources/Strings.resx @@ -267,4 +267,25 @@ Label defined multiple times. + + PInvoke methods must be static and native and cannot be abstract. + + + PInvoke methods cannot exist on interfaces. + + + Method has been already defined. + + + Global members must be static. + + + Type definition of the global function has been completed. + + + Data size must be &gt; 1 and &lt; 0x3f0000. + + + MetadataToken for the member is not generated until the assembly saved. + \ No newline at end of file diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index 8f252ace10fd7a..834d8612a949e0 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -44,7 +44,7 @@ internal static AssemblyBuilderImpl DefinePersistedAssembly(AssemblyName name, A IEnumerable? assemblyAttributes) => new AssemblyBuilderImpl(name, coreAssembly, assemblyAttributes); - private void WritePEImage(Stream peStream, BlobBuilder ilBuilder) + private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fieldData) { var peHeaderBuilder = new PEHeaderBuilder( // For now only support DLL, DLL files are considered executable files @@ -55,6 +55,7 @@ private void WritePEImage(Stream peStream, BlobBuilder ilBuilder) header: peHeaderBuilder, metadataRootBuilder: new MetadataRootBuilder(_metadataBuilder), ilStream: ilBuilder, + mappedFieldData: fieldData, strongNameSignatureSize: 0); // Write executable into the specified stream. @@ -91,10 +92,11 @@ internal void Save(Stream stream) _module.WriteCustomAttributes(_customAttributes, assemblyHandle); var ilBuilder = new BlobBuilder(); + var fieldDataBuilder = new BlobBuilder(); MethodBodyStreamEncoder methodBodyEncoder = new MethodBodyStreamEncoder(ilBuilder); - _module.AppendMetadata(methodBodyEncoder); + _module.AppendMetadata(methodBodyEncoder, fieldDataBuilder); - WritePEImage(stream, ilBuilder); + WritePEImage(stream, ilBuilder, fieldDataBuilder); _previouslySaved = true; } @@ -137,5 +139,9 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan } public override string? FullName => _assemblyName.FullName; + + public override Module ManifestModule => _module ?? throw new InvalidOperationException(SR.InvalidOperation_AModuleRequired); + + public override AssemblyName GetName(bool copiedName) => (AssemblyName)_assemblyName.Clone(); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs index 9fca44c0a608a6..5ea3676039c88a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ConstructorBuilderImpl.cs @@ -10,10 +10,11 @@ internal sealed class ConstructorBuilderImpl : ConstructorBuilder internal readonly MethodBuilderImpl _methodBuilder; internal bool _isDefaultConstructor; - public ConstructorBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConvention, - Type[]? parameterTypes, ModuleBuilderImpl mod, TypeBuilderImpl type) + public ConstructorBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConvention, Type[]? parameterTypes, + Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers, ModuleBuilderImpl module, TypeBuilderImpl type) { - _methodBuilder = new MethodBuilderImpl(name, attributes, callingConvention, null, parameterTypes, mod, type); + _methodBuilder = new MethodBuilderImpl(name, attributes, callingConvention, returnType: null, returnTypeRequiredCustomModifiers: null, + returnTypeOptionalCustomModifiers: null, parameterTypes, requiredCustomModifiers, optionalCustomModifiers, module, type); type._methodDefinitions.Add(_methodBuilder); } @@ -60,7 +61,7 @@ public override CallingConventions CallingConvention } } - public override TypeBuilder DeclaringType => _methodBuilder.DeclaringType; + public override Type DeclaringType => _methodBuilder.DeclaringType!; public override Module Module => _methodBuilder.Module; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs index dc507fcc679963..35b8e40784d0ec 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/FieldBuilderImpl.cs @@ -15,6 +15,8 @@ internal sealed class FieldBuilderImpl : FieldBuilder private readonly TypeBuilderImpl _typeBuilder; private readonly string _fieldName; private readonly Type _fieldType; + private readonly Type[]? _requiredCustomModifiers; + private readonly Type[]? _optionalCustomModifiers; private FieldAttributes _attributes; internal MarshallingData? _marshallingData; @@ -22,14 +24,17 @@ internal sealed class FieldBuilderImpl : FieldBuilder internal List? _customAttributes; internal object? _defaultValue = DBNull.Value; internal FieldDefinitionHandle _handle; + internal byte[]? _rvaData; - internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes) + internal FieldBuilderImpl(TypeBuilderImpl typeBuilder, string fieldName, Type type, FieldAttributes attributes, Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers) { _fieldName = fieldName; _typeBuilder = typeBuilder; _fieldType = type; _attributes = attributes & ~FieldAttributes.ReservedMask; _offset = -1; + _requiredCustomModifiers = requiredCustomModifiers; + _optionalCustomModifiers = optionalCustomModifiers; } protected override void SetConstantCore(object? defaultValue) @@ -37,6 +42,7 @@ protected override void SetConstantCore(object? defaultValue) _typeBuilder.ThrowIfCreated(); ValidateDefaultValueType(defaultValue, _fieldType); _defaultValue = defaultValue; + _attributes |= FieldAttributes.HasDefault; } internal static void ValidateDefaultValueType(object? defaultValue, Type destinationType) @@ -100,6 +106,12 @@ internal static void ValidateDefaultValueType(object? defaultValue, Type destina } } + internal void SetData(byte[] data) + { + _rvaData = data; + _attributes |= FieldAttributes.HasFieldRVA; + } + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { // Handle pseudo custom attributes @@ -142,9 +154,9 @@ protected override void SetOffsetCore(int iOffset) public override string Name => _fieldName; - public override Type? DeclaringType => _typeBuilder; + public override Type? DeclaringType => _typeBuilder._isHiddenGlobalType ? null : _typeBuilder; - public override Type? ReflectedType => _typeBuilder; + public override Type? ReflectedType => DeclaringType; #endregion @@ -159,6 +171,10 @@ public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, public override FieldAttributes Attributes => _attributes; + public override Type[] GetRequiredCustomModifiers() => _requiredCustomModifiers ?? Type.EmptyTypes; + + public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers ?? Type.EmptyTypes; + #endregion #region ICustomAttributeProvider Implementation diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs index 7fa928d22b689a..fccd56de74e79a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs @@ -29,12 +29,12 @@ internal GenericTypeParameterBuilderImpl(string name, int genParamPosition, Type _type = typeBuilder; } - public GenericTypeParameterBuilderImpl(string name, int genParamPosition, MethodBuilderImpl methodBuilder) + public GenericTypeParameterBuilderImpl(string name, int genParamPosition, MethodBuilderImpl methodBuilder, TypeBuilderImpl typeBuilder) { _name = name; _genParamPosition = genParamPosition; _methodBuilder = methodBuilder; - _type = methodBuilder.DeclaringType; + _type = typeBuilder; } protected override void SetBaseTypeConstraintCore([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? baseTypeConstraint) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index 6a424d04aad5b9..2862b8774e2959 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -418,12 +418,12 @@ public override void Emit(OpCode opcode, ConstructorInfo con) EmitOpcode(opcode); UpdateStackSize(stackChange); - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(con), con); + WriteOrReserveToken(_moduleBuilder.GetConstructorHandle(con), con); } - private void WriteOrReserveToken(int token, object member) + private void WriteOrReserveToken(EntityHandle handle, object member) { - if (token == -1) + if (handle.IsNil) { // The member is a `***BuilderImpl` and its token is not yet defined. // Reserve the token bytes and write them later when its ready @@ -432,7 +432,7 @@ private void WriteOrReserveToken(int token, object member) } else { - _il.Token(token); + _il.Token(MetadataTokens.GetToken(handle)); } } @@ -553,7 +553,7 @@ public override void Emit(OpCode opcode, FieldInfo field) ArgumentNullException.ThrowIfNull(field); EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetFieldMetadataToken(field), field); + WriteOrReserveToken(_moduleBuilder.GetFieldHandle(field), field); } public override void Emit(OpCode opcode, MethodInfo meth) @@ -567,7 +567,7 @@ public override void Emit(OpCode opcode, MethodInfo meth) else { EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(meth), meth); + WriteOrReserveToken(_moduleBuilder.GetMethodHandle(meth), meth); } } @@ -576,7 +576,7 @@ public override void Emit(OpCode opcode, Type cls) ArgumentNullException.ThrowIfNull(cls); EmitOpcode(opcode); - WriteOrReserveToken(_moduleBuilder.GetTypeMetadataToken(cls), cls); + WriteOrReserveToken(_moduleBuilder.GetTypeHandle(cls), cls); } public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes) @@ -592,11 +592,11 @@ public override void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? opti UpdateStackSize(GetStackChange(opcode, methodInfo, optionalParameterTypes)); if (optionalParameterTypes == null || optionalParameterTypes.Length == 0) { - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(methodInfo), methodInfo); + WriteOrReserveToken(_moduleBuilder.GetMethodHandle(methodInfo), methodInfo); } else { - WriteOrReserveToken(_moduleBuilder.GetMethodMetadataToken(methodInfo, optionalParameterTypes), + WriteOrReserveToken(_moduleBuilder.GetMethodHandle(methodInfo, optionalParameterTypes), new KeyValuePair(methodInfo, optionalParameterTypes)); } } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index 8ecc173cc109f5..a20e067e6b006a 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Runtime.InteropServices; namespace System.Reflection.Emit { @@ -23,6 +24,10 @@ internal sealed class MethodBuilderImpl : MethodBuilder private GenericTypeParameterBuilderImpl[]? _typeParameters; private ILGeneratorImpl? _ilGenerator; private bool _initLocals; + internal Type[]? _returnTypeRequiredModifiers; + internal Type[]? _returnTypeOptionalCustomModifiers; + internal Type[][]? _parameterTypeRequiredCustomModifiers; + internal Type[][]? _parameterTypeOptionalCustomModifiers; internal bool _canBeRuntimeImpl; internal DllImportData? _dllImportData; @@ -30,8 +35,10 @@ internal sealed class MethodBuilderImpl : MethodBuilder internal ParameterBuilderImpl[]? _parameterBuilders; internal MethodDefinitionHandle _handle; - internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, Type? returnType, - Type[]? parameterTypes, ModuleBuilderImpl module, TypeBuilderImpl declaringType) + internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConventions callingConventions, + Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, + Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, + ModuleBuilderImpl module, TypeBuilderImpl declaringType) { _module = module; _returnType = returnType ?? _module.GetTypeFromCoreAssembly(CoreTypeId.Void); @@ -50,9 +57,13 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv _callingConventions = callingConventions; _declaringType = declaringType; + _returnTypeRequiredModifiers = returnTypeRequiredCustomModifiers; + _returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers; if (parameterTypes != null) { + _parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers; + _parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers; _parameterTypes = new Type[parameterTypes.Length]; _parameterBuilders = new ParameterBuilderImpl[parameterTypes.Length + 1]; // parameter 0 reserved for return type for (int i = 0; i < parameterTypes.Length; i++) @@ -65,6 +76,11 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv _initLocals = true; } + internal void CreateDllImportData(string dllName, string entryName, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + _dllImportData = DllImportData.Create(dllName, entryName, nativeCallConv, nativeCharSet); + } + internal int ParameterCount => _parameterTypes == null ? 0 : _parameterTypes.Length; internal Type[]? ParameterTypes => _parameterTypes; @@ -85,32 +101,8 @@ internal MethodBuilderImpl(string name, MethodAttributes attributes, CallingConv } internal BlobBuilder GetMethodSignatureBlob() => MetadataSignatureHelper.GetMethodSignature(_module, _parameterTypes, - _returnType, GetSignatureConvention(_callingConventions, _dllImportData), GetGenericArguments().Length, !IsStatic); - - internal static SignatureCallingConvention GetSignatureConvention(CallingConventions callingConvention, DllImportData? dllImportData = null) - { - SignatureCallingConvention convention = SignatureCallingConvention.Default; - - if ((callingConvention & CallingConventions.VarArgs) != 0) - { - convention = SignatureCallingConvention.VarArgs; - } - - if (dllImportData != null) - { - // Set native call signature - convention = dllImportData.Flags switch - { - MethodImportAttributes.CallingConventionCDecl => SignatureCallingConvention.CDecl, - MethodImportAttributes.CallingConventionStdCall => SignatureCallingConvention.StdCall, - MethodImportAttributes.CallingConventionThisCall => SignatureCallingConvention.ThisCall, - MethodImportAttributes.CallingConventionFastCall => SignatureCallingConvention.FastCall, - _ => SignatureCallingConvention.Unmanaged, - }; - } - - return convention; - } + _returnType, ModuleBuilderImpl.GetSignatureConvention(_callingConventions), GetGenericArguments().Length, !IsStatic, optionalParameterTypes: null, + _returnTypeRequiredModifiers, _returnTypeOptionalCustomModifiers, _parameterTypeRequiredCustomModifiers, _parameterTypeOptionalCustomModifiers); protected override bool InitLocalsCore { @@ -130,7 +122,7 @@ protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(par { string name = names[i]; ArgumentNullException.ThrowIfNull(names, nameof(names)); - typeParameters[i] = new GenericTypeParameterBuilderImpl(name, i, this); + typeParameters[i] = new GenericTypeParameterBuilderImpl(name, i, this, _declaringType); } return _typeParameters = typeParameters; @@ -190,7 +182,7 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan return; case "System.Runtime.InteropServices.DllImportAttribute": { - _dllImportData = DllImportData.CreateDllImportData(CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute), out var preserveSig); + _dllImportData = DllImportData.Create(CustomAttributeInfo.DecodeCustomAttribute(con, binaryAttribute), out var preserveSig); _attributes |= MethodAttributes.PinvokeImpl; _canBeRuntimeImpl = true; if (preserveSig) @@ -228,6 +220,8 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq if (returnType != null) { _returnType = returnType; + _returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers; + _returnTypeRequiredModifiers = returnTypeRequiredCustomModifiers; } if (parameterTypes != null) @@ -238,14 +232,16 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq { ArgumentNullException.ThrowIfNull(_parameterTypes[i] = parameterTypes[i], nameof(parameterTypes)); } + + _parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers; + _parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers; } - // TODO: Add support for other parameters: returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers, parameterTypeRequiredCustomModifiers and parameterTypeOptionalCustomModifiers } public override string Name => _name; public override MethodAttributes Attributes => _attributes; public override CallingConventions CallingConvention => _callingConventions; - public override TypeBuilder DeclaringType => _declaringType; + public override Type? DeclaringType => _declaringType._isHiddenGlobalType ? null : _declaringType; public override Module Module => _module; public override bool ContainsGenericParameters => throw new NotSupportedException(); public override bool IsGenericMethod => _typeParameters != null; @@ -255,7 +251,7 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq public override bool IsSecurityTransparent => false; public override int MetadataToken => _handle == default ? 0 : MetadataTokens.GetToken(_handle); public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(SR.NotSupported_DynamicModule); - public override Type? ReflectedType => _declaringType; + public override Type? ReflectedType => DeclaringType; public override ParameterInfo ReturnParameter { get => throw new NotImplementedException(); } public override Type ReturnType => _returnType; public override ICustomAttributeProvider ReturnTypeCustomAttributes { get => throw new NotImplementedException(); } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index 2232bd17c61c34..cf0725c9673e04 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; namespace System.Reflection.Emit @@ -16,6 +17,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private readonly string _name; private readonly MetadataBuilder _metadataBuilder; private readonly AssemblyBuilderImpl _assemblyBuilder; + private readonly TypeBuilderImpl _globalTypeBuilder; private readonly Dictionary _assemblyReferences = new(); private readonly Dictionary _typeReferences = new(); private readonly Dictionary _memberReferences = new(); @@ -30,6 +32,7 @@ internal sealed class ModuleBuilderImpl : ModuleBuilder private int _nextPropertyRowId = 1; private int _nextEventRowId = 1; private bool _coreTypesFullyPopulated; + private bool _hasGlobalBeenCreated; private Type?[]? _coreTypes; private static readonly Type[] s_coreTypes = { typeof(void), typeof(object), typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(string), typeof(nint), typeof(nuint), typeof(TypedReference) }; @@ -41,6 +44,7 @@ internal ModuleBuilderImpl(string name, Assembly coreAssembly, MetadataBuilder b _metadataBuilder = builder; _assemblyBuilder = assemblyBuilder; _moduleVersionId = Guid.NewGuid(); + _globalTypeBuilder = new TypeBuilderImpl(this); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Types are preserved via s_coreTypes")] @@ -104,7 +108,7 @@ internal Type GetTypeFromCoreAssembly(CoreTypeId typeId) return null; } - internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) + internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder, BlobBuilder fieldDataBuilder) { // Add module metadata ModuleDefinitionHandle moduleHandle = _metadataBuilder.AddModule( @@ -124,11 +128,14 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) methodList: MetadataTokens.MethodDefinitionHandle(1)); WriteCustomAttributes(_customAttributes, moduleHandle); - - _typeDefinitions.Sort((x, y) => x.MetadataToken.CompareTo(y.MetadataToken)); - // All generic parameters for all types and methods should be written in specific order List genericParams = new(); + PopulateTokensForTypesAndItsMembers(); + + // Add global members + WriteFields(_globalTypeBuilder, fieldDataBuilder); + WriteMethods(_globalTypeBuilder._methodDefinitions, genericParams, methodBodyEncoder); + // Add each type definition to metadata table. foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) { @@ -165,7 +172,7 @@ internal void AppendMetadata(MethodBodyStreamEncoder methodBodyEncoder) WriteInterfaceImplementations(typeBuilder, typeHandle); WriteCustomAttributes(typeBuilder._customAttributes, typeHandle); WriteProperties(typeBuilder); - WriteFields(typeBuilder); + WriteFields(typeBuilder, fieldDataBuilder); WriteMethods(typeBuilder._methodDefinitions, genericParams, methodBodyEncoder); WriteEvents(typeBuilder); } @@ -316,17 +323,20 @@ private void PopulateEventDefinitionHandles(List eventDefiniti } } - internal void PopulateTypeAndItsMembersTokens(TypeBuilderImpl typeBuilder) + private void PopulateTokensForTypesAndItsMembers() { - typeBuilder._handle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); - typeBuilder._firstMethodToken = _nextMethodDefRowId; - typeBuilder._firstFieldToken = _nextFieldDefRowId; - typeBuilder._firstPropertyToken = _nextPropertyRowId; - typeBuilder._firstEventToken = _nextEventRowId; - PopulateMethodDefinitionHandles(typeBuilder._methodDefinitions); - PopulateFieldDefinitionHandles(typeBuilder._fieldDefinitions); - PopulatePropertyDefinitionHandles(typeBuilder._propertyDefinitions); - PopulateEventDefinitionHandles(typeBuilder._eventDefinitions); + foreach (TypeBuilderImpl typeBuilder in _typeDefinitions) + { + typeBuilder._handle = MetadataTokens.TypeDefinitionHandle(++_nextTypeDefRowId); + typeBuilder._firstMethodToken = _nextMethodDefRowId; + typeBuilder._firstFieldToken = _nextFieldDefRowId; + typeBuilder._firstPropertyToken = _nextPropertyRowId; + typeBuilder._firstEventToken = _nextEventRowId; + PopulateMethodDefinitionHandles(typeBuilder._methodDefinitions); + PopulateFieldDefinitionHandles(typeBuilder._fieldDefinitions); + PopulatePropertyDefinitionHandles(typeBuilder._propertyDefinitions); + PopulateEventDefinitionHandles(typeBuilder._eventDefinitions); + } } private void WriteMethods(List methods, List genericParams, MethodBodyStreamEncoder methodBodyEncoder) @@ -413,11 +423,12 @@ private static int AddMethodBody(MethodBuilderImpl method, ILGeneratorImpl il, S attributes: method.InitLocals ? MethodBodyAttributes.InitLocals : MethodBodyAttributes.None, hasDynamicStackAllocation: il.HasDynamicStackAllocation); - private void WriteFields(TypeBuilderImpl typeBuilder) + private void WriteFields(TypeBuilderImpl typeBuilder, BlobBuilder fieldDataBuilder) { foreach (FieldBuilderImpl field in typeBuilder._fieldDefinitions) { - FieldDefinitionHandle handle = AddFieldDefinition(field, MetadataSignatureHelper.GetFieldSignature(field.FieldType, this)); + FieldDefinitionHandle handle = AddFieldDefinition(field, + MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); Debug.Assert(field._handle == handle); WriteCustomAttributes(field._customAttributes, handle); @@ -426,15 +437,22 @@ private void WriteFields(TypeBuilderImpl typeBuilder) AddFieldLayout(handle, field._offset); } - if (field._marshallingData != null) + if (field.Attributes.HasFlag(FieldAttributes.HasFieldMarshal) && field._marshallingData != null) { AddMarshalling(handle, field._marshallingData.SerializeMarshallingData()); } - if (field._defaultValue != DBNull.Value) + if (field.Attributes.HasFlag(FieldAttributes.HasDefault) && field._defaultValue != DBNull.Value) { AddDefaultValue(handle, field._defaultValue); } + + if (field.Attributes.HasFlag(FieldAttributes.HasFieldRVA) && field._rvaData != null) + { + _metadataBuilder.AddFieldRelativeVirtualAddress(handle, fieldDataBuilder.Count); + fieldDataBuilder.WriteBytes(field._rvaData!); + fieldDataBuilder.Align(ManagedPEBuilder.MappedFieldDataAlignment); + } } } @@ -501,17 +519,19 @@ private MethodSpecificationHandle AddMethodSpecification(EntityHandle methodHand method: methodHandle, instantiation: _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetMethodSpecificationSignature(genericArgs, this))); - private EntityHandle GetMemberReferenceHandle(MemberInfo member) + private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) { - if (!_memberReferences.TryGetValue(member, out var memberHandle)) + if (!_memberReferences.TryGetValue(memberInfo, out var memberHandle)) { + MemberInfo member = GetOriginalMemberIfConstructedType(memberInfo); switch (member) { case FieldInfo field: - memberHandle = AddMemberReference(field.Name, GetTypeHandle(field.DeclaringType!), MetadataSignatureHelper.GetFieldSignature(field.FieldType, this)); + memberHandle = AddMemberReference(field.Name, GetTypeHandle(memberInfo.DeclaringType!), + MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); break; case ConstructorInfo ctor: - memberHandle = AddMemberReference(ctor.Name, GetTypeHandle(ctor.DeclaringType!), GetMemberSignature(ctor)); + memberHandle = AddMemberReference(ctor.Name, GetTypeHandle(memberInfo.DeclaringType!), MetadataSignatureHelper.GetConstructorSignature(ctor.GetParameters(), this)); break; case MethodInfo method: if (method.IsConstructedGenericMethod) @@ -520,7 +540,7 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo member) } else { - memberHandle = AddMemberReference(member.Name, GetTypeHandle(member.DeclaringType!), GetMemberSignature(member)); + memberHandle = AddMemberReference(method.Name, GetTypeHandle(memberInfo.DeclaringType!), GetMethodSignature(method, null)); } break; default: @@ -528,7 +548,7 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo member) } - _memberReferences.Add(member, memberHandle); + _memberReferences.Add(memberInfo, memberHandle); } return memberHandle; @@ -551,39 +571,29 @@ private EntityHandle GetMethodReference(MethodInfo methodInfo, Type[] optionalPa private BlobBuilder GetMethodSignature(MethodInfo method, Type[]? optionalParameterTypes) => MetadataSignatureHelper.GetMethodSignature(this, ParameterTypes(method.GetParameters()), method.ReturnType, - MethodBuilderImpl.GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic, optionalParameterTypes); + GetSignatureConvention(method.CallingConvention), method.GetGenericArguments().Length, !method.IsStatic, optionalParameterTypes); - private static MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) + internal static SignatureCallingConvention GetSignatureConvention(CallingConventions callingConvention) { - Type declaringType = memberInfo.DeclaringType!; - if (declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) + SignatureCallingConvention convention = SignatureCallingConvention.Default; + + if ((callingConvention & CallingConventions.VarArgs) != 0) { - return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo); + convention = SignatureCallingConvention.VarArgs; } - return memberInfo; + return convention; } - private BlobBuilder GetMemberSignature(MemberInfo memberInfo) + private static MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) { - MemberInfo member = GetOriginalMemberIfConstructedType(memberInfo); - - if (member is MethodInfo method) - { - return GetMethodSignature(method, optionalParameterTypes: null); - } - - if (member is FieldInfo field) - { - return MetadataSignatureHelper.GetFieldSignature(field.FieldType, this); - } - - if (member is ConstructorInfo ctor) + Type declaringType = memberInfo.DeclaringType!; + if (declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) { - return MetadataSignatureHelper.GetConstructorSignature(ctor.GetParameters(), this); + return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo); } - throw new NotSupportedException(); + return memberInfo; } private static Type[] ParameterTypes(ParameterInfo[] parameterInfos) @@ -608,7 +618,19 @@ private AssemblyReferenceHandle GetAssemblyReference(Assembly assembly) if (!_assemblyReferences.TryGetValue(assembly, out var handle)) { AssemblyName aName = assembly.GetName(); - handle = AddAssemblyReference(aName.Name, aName.Version, aName.CultureName, aName.GetPublicKeyToken(), aName.Flags, aName.ContentType); + // For ref assembly flags shall have only one bit set, the PublicKey bit. + // All other bits shall be zero. See ECMA-335 spec II.22.5 + AssemblyFlags assemblyFlags = 0; + byte[]? publicKeyOrToken = aName.GetPublicKey(); + if (publicKeyOrToken != null && publicKeyOrToken.Length > 0) + { + assemblyFlags = AssemblyFlags.PublicKey; + } + else + { + publicKeyOrToken = aName.GetPublicKeyToken(); + } + handle = AddAssemblyReference(aName.Name, aName.Version, aName.CultureName, publicKeyOrToken, assemblyFlags); _assemblyReferences.Add(assembly, handle); } @@ -721,13 +743,13 @@ private ParameterHandle AddParameter(ParameterBuilderImpl parameter) => sequenceNumber: parameter.Position); private AssemblyReferenceHandle AddAssemblyReference(string? name, Version? version, string? culture, - byte[]? publicKeyToken, AssemblyNameFlags flags, AssemblyContentType contentType) => + byte[]? publicKeyToken, AssemblyFlags assemblyFlags) => _metadataBuilder.AddAssemblyReference( name: name == null ? default : _metadataBuilder.GetOrAddString(name), version: version ?? new Version(0, 0, 0, 0), culture: (culture == null) ? default : _metadataBuilder.GetOrAddString(value: culture), - publicKeyOrToken: (publicKeyToken == null) ? default : _metadataBuilder.GetOrAddBlob(publicKeyToken), // reference has token, not full public key - flags: (AssemblyFlags)((int)contentType << 9) | ((flags & AssemblyNameFlags.Retargetable) != 0 ? AssemblyFlags.Retargetable : 0), + publicKeyOrToken: (publicKeyToken == null) ? default : _metadataBuilder.GetOrAddBlob(publicKeyToken), + flags: assemblyFlags, hashValue: default); // .file directive assemblies not supported, no need to handle this value. internal EntityHandle GetTypeHandle(Type type) @@ -800,58 +822,69 @@ internal TypeBuilder DefineNestedType(string name, TypeAttributes attr, [Dynamic public override Guid ModuleVersionId => _moduleVersionId; public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); - public override int GetFieldMetadataToken(FieldInfo field) + public override int GetFieldMetadataToken(FieldInfo field) => GetTokenForHandle(GetFieldHandle(field)); + + internal EntityHandle GetFieldHandle(FieldInfo field) { if (field is FieldBuilderImpl fb) { - return fb._handle != default ? MetadataTokens.GetToken(fb._handle) : -1; + return fb._handle; } - if (IsConstuctedFromNotBakedTypeBuilder(field.DeclaringType!)) + return GetHandleForMember(field); + } + + private static int GetTokenForHandle(EntityHandle handle) + { + if (handle.IsNil) { - return -1; + throw new InvalidOperationException(SR.InvalidOperation_TokenNotPopulated); } - return MetadataTokens.GetToken(GetMemberReferenceHandle(field)); + return MetadataTokens.GetToken(handle); } - private static bool IsConstuctedFromNotBakedTypeBuilder(Type type) => type.IsConstructedGenericType && - type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default; - - public override int GetMethodMetadataToken(ConstructorInfo constructor) + private EntityHandle GetHandleForMember(MemberInfo member) { - if (constructor is ConstructorBuilderImpl cb) + if (IsConstructedFromNotBakedTypeBuilder(member.DeclaringType!)) { - return cb._methodBuilder._handle != default ? MetadataTokens.GetToken(cb._methodBuilder._handle) : -1; + return default; } - if (IsConstuctedFromNotBakedTypeBuilder(constructor.DeclaringType!)) + return GetMemberReferenceHandle(member); + } + + private static bool IsConstructedFromNotBakedTypeBuilder(Type type) => type.IsConstructedGenericType && + type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default; + + public override int GetMethodMetadataToken(ConstructorInfo constructor) => GetTokenForHandle(GetConstructorHandle(constructor)); + + internal EntityHandle GetConstructorHandle(ConstructorInfo constructor) + { + if (constructor is ConstructorBuilderImpl cb) { - return -1; + return cb._methodBuilder._handle; } - return MetadataTokens.GetToken(GetMemberReferenceHandle(constructor)); + return GetHandleForMember(constructor); } - public override int GetMethodMetadataToken(MethodInfo method) + public override int GetMethodMetadataToken(MethodInfo method) => GetTokenForHandle(GetMethodHandle(method)); + + internal EntityHandle GetMethodHandle(MethodInfo method) { if (method is MethodBuilderImpl mb) { - return mb._handle != default ? MetadataTokens.GetToken(mb._handle) : -1; - } - - if (IsConstructedMethodFromNotBakedMethodBuilder(method)) - { - return -1; + return mb._handle; } - return MetadataTokens.GetToken(GetMemberReferenceHandle(method)); + return GetHandleForMember(method); } private static bool IsConstructedMethodFromNotBakedMethodBuilder(MethodInfo method) => method.IsConstructedGenericMethod && method.GetGenericMethodDefinition() is MethodBuilderImpl mb2 && mb2._handle == default; - internal int GetMethodMetadataToken(MethodInfo method, Type[] optionalParameterTypes) + internal EntityHandle GetMethodHandle(MethodInfo method, Type[] optionalParameterTypes) { if ((method.CallingConvention & CallingConventions.VarArgs) == 0) { @@ -859,43 +892,104 @@ internal int GetMethodMetadataToken(MethodInfo method, Type[] optionalParameterT throw new InvalidOperationException(SR.InvalidOperation_NotAVarArgCallingConvention); } - if (method is MethodBuilderImpl mb && mb._handle == default || IsConstructedMethodFromNotBakedMethodBuilder(method)) + if (method is MethodBuilderImpl mb) + { + return mb._handle; + } + + if (IsConstructedMethodFromNotBakedMethodBuilder(method)) { - return -1; + return default; } - return MetadataTokens.GetToken(GetMethodReference(method, optionalParameterTypes)); + return GetMethodReference(method, optionalParameterTypes); } - public override int GetStringMetadataToken(string stringConstant) => MetadataTokens.GetToken(_metadataBuilder.GetOrAddUserString(stringConstant)); - - public override int GetTypeMetadataToken(Type type) + internal TypeBuilderImpl? FindTypeBuilderWithName(string strTypeName, bool ignoreCase) { - if (type is TypeBuilderImpl tb && Equals(tb.Module)) + StringComparison casing = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + + foreach (TypeBuilderImpl type in _typeDefinitions) { - return tb._handle != default ? MetadataTokens.GetToken(tb._handle) : -1; + if (string.Equals(type.Name, strTypeName, casing)) + { + return type; + } } - if (type is EnumBuilderImpl eb && Equals(eb.Module)) + return null; + } + + public override int GetStringMetadataToken(string stringConstant) => MetadataTokens.GetToken(_metadataBuilder.GetOrAddUserString(stringConstant)); + + public override int GetTypeMetadataToken(Type type) => GetTokenForHandle(GetTypeHandle(type)); + + protected override void CreateGlobalFunctionsCore() + { + if (_hasGlobalBeenCreated) { - return eb._typeBuilder._handle != default ? MetadataTokens.GetToken(eb._typeBuilder._handle) : -1; + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); } - return MetadataTokens.GetToken(GetTypeReferenceOrSpecificationHandle(type)); + _globalTypeBuilder.CreateTypeInfo(); + _hasGlobalBeenCreated = true; } - protected override void CreateGlobalFunctionsCore() => throw new NotImplementedException(); - protected override EnumBuilder DefineEnumCore(string name, TypeAttributes visibility, Type underlyingType) { EnumBuilderImpl enumBuilder = new EnumBuilderImpl(name, underlyingType, visibility, this); _typeDefinitions.Add(enumBuilder._typeBuilder); return enumBuilder; } - protected override MethodBuilder DefineGlobalMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers, Type[]? parameterTypes, Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers) => throw new NotImplementedException(); - protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) => throw new NotImplementedException(); + + protected override MethodBuilder DefineGlobalMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, + Type? returnType, Type[]? requiredReturnTypeCustomModifiers, Type[]? optionalReturnTypeCustomModifiers, Type[]? parameterTypes, + Type[][]? requiredParameterTypeCustomModifiers, Type[][]? optionalParameterTypeCustomModifiers) + { + if (_hasGlobalBeenCreated) + { + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); + } + + if ((attributes & MethodAttributes.Static) == 0) + { + throw new ArgumentException(SR.Argument_GlobalMembersMustBeStatic); + } + + MethodBuilderImpl method = (MethodBuilderImpl)_globalTypeBuilder.DefineMethod(name, attributes, callingConvention, + returnType, requiredReturnTypeCustomModifiers, optionalReturnTypeCustomModifiers, + parameterTypes, requiredParameterTypeCustomModifiers, optionalParameterTypeCustomModifiers); + method._handle = MetadataTokens.MethodDefinitionHandle(_nextMethodDefRowId++); + return method; + } + + protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) + { + if (_hasGlobalBeenCreated) + { + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); + } + + FieldBuilderImpl field = (FieldBuilderImpl)_globalTypeBuilder.DefineInitializedData(name, data, attributes); + field._handle = MetadataTokens.FieldDefinitionHandle(_nextFieldDefRowId++); + return field; + } + [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] - protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); + protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, + CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + // Global methods must be static. + if ((attributes & MethodAttributes.Static) == 0) + { + throw new ArgumentException(SR.Argument_GlobalMembersMustBeStatic); + } + + MethodBuilderImpl method = (MethodBuilderImpl)_globalTypeBuilder.DefinePInvokeMethod( + name, dllName, entryName, attributes, callingConvention, returnType, parameterTypes, nativeCallConv, nativeCharSet); + method._handle = MetadataTokens.MethodDefinitionHandle(_nextMethodDefRowId++); + return method; + } protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, Type[]? interfaces, PackingSize packingSize, int typesize) @@ -905,7 +999,18 @@ protected override TypeBuilder DefineTypeCore(string name, TypeAttributes attr, return _type; } - protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); + protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) + { + if (_hasGlobalBeenCreated) + { + throw new InvalidOperationException(SR.InvalidOperation_GlobalsHaveBeenCreated); + } + + FieldBuilderImpl field = (FieldBuilderImpl)_globalTypeBuilder.DefineUninitializedData(name, size, attributes); + field._handle = MetadataTokens.FieldDefinitionHandle(_nextFieldDefRowId++); + return field; + } + protected override MethodInfo GetArrayMethodCore(Type arrayClass, string methodName, CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes) => throw new NotImplementedException(); protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { @@ -913,13 +1018,31 @@ protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } + [RequiresUnreferencedCode("Methods might be removed")] + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, + CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) + { + if (types == null) + { + return _globalTypeBuilder.GetMethod(name, bindingAttr); + } + + return _globalTypeBuilder.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); + } + + [RequiresUnreferencedCode("Methods might be removed")] + public override MethodInfo[] GetMethods(BindingFlags bindingFlags) + { + return _globalTypeBuilder.GetMethods(bindingFlags); + } + public override int GetSignatureMetadataToken(SignatureHelper signature) => MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob(signature.GetSignature()))); - internal int GetSignatureToken(CallingConventions callingConvention, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => + internal int GetSignatureToken(CallingConventions callingConventions, Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes) => MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature( _metadataBuilder.GetOrAddBlob(MetadataSignatureHelper.GetMethodSignature(this, parameterTypes, returnType, - MethodBuilderImpl.GetSignatureConvention(callingConvention), optionalParameterTypes: optionalParameterTypes)))); + GetSignatureConvention(callingConventions), optionalParameterTypes: optionalParameterTypes)))); internal int GetSignatureToken(CallingConvention callingConvention, Type? returnType, Type[]? parameterTypes) => MetadataTokens.GetToken(_metadataBuilder.AddStandaloneSignature(_metadataBuilder.GetOrAddBlob( diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs index 54c5a80079d5ee..97de7d45d44911 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PropertyBuilderImpl.cs @@ -18,12 +18,15 @@ internal sealed class PropertyBuilderImpl : PropertyBuilder private MethodInfo? _getMethod; private MethodInfo? _setMethod; internal HashSet? _otherMethods; - + internal readonly Type[]? _returnTypeRequiredCustomModifiers; + internal readonly Type[]? _returnTypeOptionalCustomModifiers; + internal readonly Type[][]? _parameterTypeRequiredCustomModifiers; + internal readonly Type[][]? _parameterTypeOptionalCustomModifiers; internal PropertyDefinitionHandle _handle; internal List? _customAttributes; internal object? _defaultValue = DBNull.Value; - internal PropertyBuilderImpl(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? parameterTypes, TypeBuilderImpl containingType) + internal PropertyBuilderImpl(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, TypeBuilderImpl containingType) { ArgumentException.ThrowIfNullOrEmpty(name); @@ -33,6 +36,10 @@ internal PropertyBuilderImpl(string name, PropertyAttributes attributes, Calling _propertyType = returnType; _parameterTypes = parameterTypes; _containingType = containingType; + _returnTypeRequiredCustomModifiers = returnTypeRequiredCustomModifiers; + _returnTypeOptionalCustomModifiers = returnTypeOptionalCustomModifiers; + _parameterTypeRequiredCustomModifiers = parameterTypeRequiredCustomModifiers; + _parameterTypeOptionalCustomModifiers = parameterTypeOptionalCustomModifiers; } internal Type[]? ParameterTypes => _parameterTypes; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs index bd95b6aee4c82e..e6f3b480f89523 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/PseudoCustomAttributesData.cs @@ -26,7 +26,7 @@ internal DllImportData(string moduleName, string? entryPoint, MethodImportAttrib public MethodImportAttributes Flags => _flags; - internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out bool preserveSig) + internal static DllImportData Create(CustomAttributeInfo attr, out bool preserveSig) { string? moduleName = (string?)attr._ctorArgs[0]; if (string.IsNullOrEmpty(moduleName)) @@ -47,23 +47,10 @@ internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out preserveSig = (bool)value; break; case "CallingConvention": - importAttributes |= (CallingConvention)value switch - { - CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, - CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, - CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, - CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, - _ => MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this - }; + importAttributes |= MatchNativeCallingConvention((CallingConvention)value); break; case "CharSet": - importAttributes |= (CharSet)value switch - { - CharSet.Ansi => MethodImportAttributes.CharSetAnsi, - CharSet.Auto => MethodImportAttributes.CharSetAuto, - CharSet.Unicode => MethodImportAttributes.CharSetUnicode, - _ => MethodImportAttributes.CharSetAuto - }; + importAttributes |= MatchNativeCharSet((CharSet)value); break; case "EntryPoint": entryPoint = (string?)value; @@ -105,6 +92,38 @@ internal static DllImportData CreateDllImportData(CustomAttributeInfo attr, out return new DllImportData(moduleName, entryPoint, importAttributes); } + + internal static DllImportData Create(string moduleName, string entryName, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + if (string.IsNullOrEmpty(moduleName)) + { + throw new ArgumentException(SR.Argument_DllNameCannotBeEmpty); + } + + MethodImportAttributes importAttributes = MatchNativeCallingConvention(nativeCallConv); + importAttributes |= MatchNativeCharSet(nativeCharSet); + + return new DllImportData(moduleName, entryName, importAttributes); + } + + private static MethodImportAttributes MatchNativeCharSet(CharSet nativeCharSet) => + nativeCharSet switch + { + CharSet.Ansi => MethodImportAttributes.CharSetAnsi, + CharSet.Auto => MethodImportAttributes.CharSetAuto, + CharSet.Unicode => MethodImportAttributes.CharSetUnicode, + _ => MethodImportAttributes.CharSetAuto + }; + + private static MethodImportAttributes MatchNativeCallingConvention(CallingConvention nativeCallConv) => + nativeCallConv switch + { + CallingConvention.Cdecl => MethodImportAttributes.CallingConventionCDecl, + CallingConvention.FastCall => MethodImportAttributes.CallingConventionFastCall, + CallingConvention.StdCall => MethodImportAttributes.CallingConventionStdCall, + CallingConvention.ThisCall => MethodImportAttributes.CallingConventionThisCall, + _ => MethodImportAttributes.CallingConventionWinApi // Roslyn defaults with this + }; } internal sealed class MarshallingData 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 2fd790cd324df8..c0866d997cf9ab 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 @@ -24,10 +24,12 @@ internal static BlobBuilder GetLocalSignature(List locals, ModuleB return localSignature; } - internal static BlobBuilder GetFieldSignature(Type fieldType, ModuleBuilderImpl module) + internal static BlobBuilder GetFieldSignature(Type fieldType, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, ModuleBuilderImpl module) { BlobBuilder fieldSignature = new(); - WriteSignatureForType(new BlobEncoder(fieldSignature).Field().Type(), fieldType, module); + FieldTypeEncoder encoder = new BlobEncoder(fieldSignature).Field(); + WriteReturnTypeCustomModifiers(encoder.CustomModifiers(), requiredCustomModifiers, optionalCustomModifiers, module); + WriteSignatureForType(encoder.Type(), fieldType, module); return fieldSignature; } @@ -70,8 +72,9 @@ internal static BlobBuilder GetMethodSpecificationSignature(Type[] genericArgume return methodSpecSignature; } - internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, - SignatureCallingConvention convention, int genParamCount = 0, bool isInstance = false, Type[]? optionalParameterTypes = null) + internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? parameters, Type? returnType, SignatureCallingConvention convention, + int genParamCount = 0, bool isInstance = false, Type[]? optionalParameterTypes = null, Type[]? returnTypeRequiredModifiers = null, + Type[]? returnTypeOptionalModifiers = null, Type[][]? parameterRequiredModifiers = null, Type[][]? parameterOptionalModifiers = null) { BlobBuilder methodSignature = new(); @@ -80,6 +83,8 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? new BlobEncoder(methodSignature).MethodSignature(convention, genParamCount, isInstance). Parameters(paramsLength, out ReturnTypeEncoder retEncoder, out ParametersEncoder parEncoder); + WriteReturnTypeCustomModifiers(retEncoder.CustomModifiers(), returnTypeRequiredModifiers, returnTypeOptionalModifiers, module); + if (returnType != null && returnType != module.GetTypeFromCoreAssembly(CoreTypeId.Void)) { WriteSignatureForType(retEncoder.Type(), returnType, module); @@ -89,7 +94,7 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? retEncoder.Void(); } - WriteParametersSignature(module, parameters, parEncoder); + WriteParametersSignature(module, parameters, parEncoder, parameterRequiredModifiers, parameterOptionalModifiers); if (optionalParameterTypes != null && optionalParameterTypes.Length != 0) { @@ -99,13 +104,48 @@ internal static BlobBuilder GetMethodSignature(ModuleBuilderImpl module, Type[]? return methodSignature; } - private static void WriteParametersSignature(ModuleBuilderImpl module, Type[]? parameters, ParametersEncoder parameterEncoder) + private static void WriteReturnTypeCustomModifiers(CustomModifiersEncoder encoder, + Type[]? requiredModifiers, Type[]? optionalModifiers, ModuleBuilderImpl module) + { + if (requiredModifiers != null) + { + WriteCustomModifiers(encoder, requiredModifiers, isOptional: false, module); + } + + if (optionalModifiers != null) + { + WriteCustomModifiers(encoder, optionalModifiers, isOptional: true, module); + } + } + + private static void WriteCustomModifiers(CustomModifiersEncoder encoder, Type[] customModifiers, bool isOptional, ModuleBuilderImpl module) + { + foreach (Type modifier in customModifiers) + { + encoder.AddModifier(module.GetTypeHandle(modifier), isOptional); + } + } + + private static void WriteParametersSignature(ModuleBuilderImpl module, Type[]? parameters, + ParametersEncoder parameterEncoder, Type[][]? requiredModifiers = null, Type[][]? optionalModifiers = null) { if (parameters != null) // If parameters null, just keep the ParametersEncoder empty { - foreach (Type parameter in parameters) + for (int i = 0; i < parameters.Length; i++) { - WriteSignatureForType(parameterEncoder.AddParameter().Type(), parameter, module); + ParameterTypeEncoder encoder = parameterEncoder.AddParameter(); + + if (requiredModifiers != null && requiredModifiers.Length > i && requiredModifiers[i] != null) + { + WriteCustomModifiers(encoder.CustomModifiers(), requiredModifiers[i], isOptional: false, module); + } + + if (optionalModifiers != null && optionalModifiers.Length > i && optionalModifiers[i] != null) + { + WriteCustomModifiers(encoder.CustomModifiers(), optionalModifiers[i], isOptional: true, module); + } + + WriteSignatureForType(encoder.Type(), parameters[i], module); } } } @@ -118,8 +158,9 @@ internal static BlobBuilder GetPropertySignature(PropertyBuilderImpl property, M PropertySignature(isInstanceProperty: property.CallingConventions.HasFlag(CallingConventions.HasThis)). Parameters(property.ParameterTypes == null ? 0 : property.ParameterTypes.Length, out ReturnTypeEncoder retType, out ParametersEncoder paramEncoder); + WriteReturnTypeCustomModifiers(retType.CustomModifiers(), property._returnTypeRequiredCustomModifiers, property._returnTypeOptionalCustomModifiers, module); WriteSignatureForType(retType.Type(), property.PropertyType, module); - WriteParametersSignature(module, property.ParameterTypes, paramEncoder); + WriteParametersSignature(module, property.ParameterTypes, paramEncoder, property._parameterTypeRequiredCustomModifiers, property._parameterTypeOptionalCustomModifiers); return propertySignature; } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 1cf4d6009408e3..176a04a217dec6 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -26,6 +26,7 @@ internal sealed class TypeBuilderImpl : TypeBuilder private Type? _enumUnderlyingType; private bool _isCreated; + internal bool _isHiddenGlobalType; internal TypeDefinitionHandle _handle; internal int _firstFieldToken; internal int _firstMethodToken; @@ -40,6 +41,15 @@ internal sealed class TypeBuilderImpl : TypeBuilder internal List? _customAttributes; internal Dictionary>? _methodOverrides; + // Only for creating the global type + internal TypeBuilderImpl(ModuleBuilderImpl module) + { + _name = ""; // Defined in ECMA-335 II.10.8 + _module = module; + _isHiddenGlobalType = true; + _handle = MetadataTokens.TypeDefinitionHandle(1); + } + internal TypeBuilderImpl(string fullName, TypeAttributes typeAttributes, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? parent, ModuleBuilderImpl module, Type[]? interfaces, PackingSize packingSize, int typeSize, TypeBuilderImpl? enclosingType) @@ -94,6 +104,12 @@ protected override TypeInfo CreateTypeInfoCore() return this; } + if (_isHiddenGlobalType) + { + _isCreated = true; + return null!; + } + // Create a public default constructor if this class has no constructor. Except the type is Interface, ValueType, Enum, or a static class. // (TypeAttributes.Abstract | TypeAttributes.Sealed) determines if the type is static if (_constructorDefinitions.Count == 0 && (_attributes & TypeAttributes.Interface) == 0 && !IsValueType && @@ -102,7 +118,6 @@ protected override TypeInfo CreateTypeInfoCore() DefineDefaultConstructor(MethodAttributes.Public); } - _module.PopulateTypeAndItsMembersTokens(this); ValidateMethods(); _isCreated = true; @@ -182,11 +197,16 @@ private void CheckInterfaces(Type[] _interfaces) foreach (Type interfaceType in _interfaces) { #pragma warning disable IL2075 // Analyzer produces a different warning code than illink. The IL2065 suppression takes care of illink: https://github.com/dotnet/runtime/issues/96646 - MethodInfo[] interfaceMethods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo[] interfaceMethods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); #pragma warning restore IL2075 for (int i = 0; i < interfaceMethods.Length; i++) { MethodInfo interfaceMethod = interfaceMethods[i]; + if (!interfaceMethod.IsAbstract) + { + continue; + } + MethodInfo? implementedMethod = GetMethodImpl(interfaceMethod.Name, GetBindingFlags(interfaceMethod), null, interfaceMethod.CallingConvention, GetParameterTypes(interfaceMethod.GetParameters()), null); if ((implementedMethod == null || implementedMethod.IsAbstract) && !FoundInInterfaceMapping(interfaceMethod)) @@ -231,7 +251,8 @@ internal void ThrowIfCreated() } } - protected override ConstructorBuilder DefineConstructorCore(MethodAttributes attributes, CallingConventions callingConvention, Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers) + protected override ConstructorBuilder DefineConstructorCore(MethodAttributes attributes, CallingConventions callingConvention, + Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers) { if ((_attributes & TypeAttributes.Interface) == TypeAttributes.Interface && (attributes & MethodAttributes.Static) != MethodAttributes.Static) { @@ -251,7 +272,8 @@ protected override ConstructorBuilder DefineConstructorCore(MethodAttributes att } attributes |= MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; - ConstructorBuilderImpl constBuilder = new ConstructorBuilderImpl(name, attributes, callingConvention, parameterTypes, _module, this); + ConstructorBuilderImpl constBuilder = new ConstructorBuilderImpl(name, attributes, callingConvention, + parameterTypes, requiredCustomModifiers, optionalCustomModifiers, _module, this); _constructorDefinitions.Add(constBuilder); return constBuilder; } @@ -322,7 +344,7 @@ protected override FieldBuilder DefineFieldCore(string fieldName, Type type, Typ } } - var field = new FieldBuilderImpl(this, fieldName, type, attributes); + var field = new FieldBuilderImpl(this, fieldName, type, attributes, requiredCustomModifiers, optionalCustomModifiers); _fieldDefinitions.Add(field); return field; } @@ -345,13 +367,28 @@ protected override GenericTypeParameterBuilder[] DefineGenericParametersCore(par return _typeParameters = typeParameters; } - protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) => throw new NotImplementedException(); + protected override FieldBuilder DefineInitializedDataCore(string name, byte[] data, FieldAttributes attributes) + { + if (data.Length <= 0 || data.Length >= 0x003f0000) + { + throw new ArgumentException(SR.Argument_BadSizeForData, nameof(data.Length)); + } + + // This method will define an initialized Data in .sdata. + // We will create a fake TypeDef to represent the data with size. This TypeDef + // will be the signature for the Field. + return DefineDataHelper(name, data, data.Length, attributes); + } - protected override MethodBuilder DefineMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) + protected override MethodBuilder DefineMethodCore(string name, MethodAttributes attributes, CallingConventions callingConvention, + Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, + Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) { ThrowIfCreated(); - MethodBuilderImpl methodBuilder = new(name, attributes, callingConvention, returnType, parameterTypes, _module, this); + + MethodBuilderImpl methodBuilder = new(name, attributes, callingConvention, returnType, returnTypeRequiredCustomModifiers, + returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers, _module, this); _methodDefinitions.Add(methodBuilder); return methodBuilder; } @@ -466,19 +503,98 @@ protected override TypeBuilder DefineNestedTypeCore(string name, TypeAttributes } [RequiresUnreferencedCode("P/Invoke marshalling may dynamically access members that could be trimmed.")] - protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, CallingConvention nativeCallConv, CharSet nativeCharSet) => throw new NotImplementedException(); + protected override MethodBuilder DefinePInvokeMethodCore(string name, string dllName, string entryName, MethodAttributes attributes, + CallingConventions callingConvention, Type? returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, + Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers, CallingConvention nativeCallConv, CharSet nativeCharSet) + { + if ((attributes & MethodAttributes.Abstract) != 0) + { + throw new ArgumentException(SR.Argument_BadPInvokeMethod); + } + + if ((_attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) + { + throw new ArgumentException(SR.Argument_BadPInvokeOnInterface); + } + + ThrowIfCreated(); + + attributes |= MethodAttributes.PinvokeImpl; + MethodBuilderImpl method = new MethodBuilderImpl(name, attributes, callingConvention, returnType, returnTypeRequiredCustomModifiers, + returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers, _module, this); + method.CreateDllImportData(dllName, entryName, nativeCallConv, nativeCharSet); + + if (_methodDefinitions.Contains(method)) + { + throw new ArgumentException(SR.Argument_MethodRedefined); + } + _methodDefinitions.Add(method); - protected override PropertyBuilder DefinePropertyCore(string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[]? returnTypeRequiredCustomModifiers, - Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) + return method; + } + + protected override PropertyBuilder DefinePropertyCore(string name, PropertyAttributes attributes, CallingConventions callingConvention, + Type returnType, Type[]? returnTypeRequiredCustomModifiers, Type[]? returnTypeOptionalCustomModifiers, Type[]? parameterTypes, + Type[][]? parameterTypeRequiredCustomModifiers, Type[][]? parameterTypeOptionalCustomModifiers) { - PropertyBuilderImpl property = new PropertyBuilderImpl(name, attributes, callingConvention, returnType, parameterTypes, this); + PropertyBuilderImpl property = new PropertyBuilderImpl(name, attributes, callingConvention, returnType, returnTypeRequiredCustomModifiers, + returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers, this); _propertyDefinitions.Add(property); return property; } - protected override ConstructorBuilder DefineTypeInitializerCore() => throw new NotImplementedException(); - protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) => throw new NotImplementedException(); + protected override ConstructorBuilder DefineTypeInitializerCore() + { + ThrowIfCreated(); + + const MethodAttributes attr = MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + + return new ConstructorBuilderImpl(ConstructorInfo.TypeConstructorName, attr, CallingConventions.Standard, null, null, null, _module, this); + } + + protected override FieldBuilder DefineUninitializedDataCore(string name, int size, FieldAttributes attributes) + { + if (size <= 0 || size >= 0x003f0000) + { + throw new ArgumentException(SR.Argument_BadSizeForData, nameof(size)); + } + + // This method will define an uninitialized Data in .sdata. + // We will create a fake TypeDef to represent the data with size. This TypeDef + // will be the signature for the Field. + return DefineDataHelper(name, new byte[size], size, attributes); + } + + private FieldBuilder DefineDataHelper(string name, byte[] data, int size, FieldAttributes attributes) + { + ArgumentException.ThrowIfNullOrEmpty(name); + + ThrowIfCreated(); + + // form the value class name + string strValueClassName = $"$ArrayType${size}"; + + // Is this already defined in this module? + TypeBuilderImpl? valueClassType = _module.FindTypeBuilderWithName(strValueClassName, false); + + if (valueClassType == null) + { + TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.ExplicitLayout | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.AnsiClass; + + // Define the backing value class + valueClassType = (TypeBuilderImpl)_module.DefineType(strValueClassName, typeAttributes, typeof(ValueType), PackingSize.Size1, size); + valueClassType.CreateType(); + } + + FieldBuilder fdBuilder = DefineField(name, valueClassType, attributes | FieldAttributes.Static | FieldAttributes.HasFieldRVA); + + // now we need to set the RVA + ((FieldBuilderImpl)fdBuilder).SetData(data); + return fdBuilder; + } + protected override bool IsCreatedCore() => _isCreated; + protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan binaryAttribute) { // Handle pseudo custom attributes @@ -618,7 +734,8 @@ public override Type GetGenericTypeDefinition() public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); public override object[] GetCustomAttributes(bool inherit) => throw new NotImplementedException(); public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException(); - public override Type GetElementType() => throw new NotSupportedException(); + // You will never have to deal with a TypeBuilder if you are just referring to arrays. + public override Type GetElementType() => throw new NotSupportedException(SR.NotSupported_DynamicModule); public override string? AssemblyQualifiedName => throw new NotSupportedException(); public override string? FullName => _strFullName ??= TypeNameBuilder.ToString(this, TypeNameBuilder.Format.FullName); public override string? Namespace => _namespace; diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs index 96016fddcab86a..4fca521e6a352c 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs @@ -134,16 +134,16 @@ public void AssemblyWithDifferentTypes() fb.SetCustomAttribute(cattrb); tbFields.CreateType(); - // Data TODO: not supported yet - //module.DefineUninitializedData("data1", 16, FieldAttributes.Public); - //module.DefineInitializedData("data2", new byte[] { 1, 2, 3, 4, 5, 6 }, FieldAttributes.Public); + // Data + module.DefineUninitializedData("Data1", 16, FieldAttributes.Public); + module.DefineInitializedData("Data2", new byte[] { 1, 2, 3, 4, 5, 6 }, FieldAttributes.Public); // Methods and signatures TypeBuilder tb5 = module.DefineType("TypeMethods", TypeAttributes.Public, typeof(object)); // .ctor var cmodsReq1 = new Type[] { typeof(object) }; var cmodsOpt1 = new Type[] { typeof(int) }; - var ctorb = tb5.DefineConstructor(MethodAttributes.Public | MethodAttributes.RTSpecialName, CallingConventions.HasThis, [typeof(int), typeof(object)], new Type[][] { cmodsReq1, null }, new Type[][] { cmodsOpt1, null }); + var ctorb = tb5.DefineConstructor(MethodAttributes.Public | MethodAttributes.RTSpecialName, CallingConventions.HasThis, [typeof(int), typeof(object)], [cmodsReq1, null], [cmodsOpt1, null]); ctorb.SetImplementationFlags(MethodImplAttributes.NoInlining); ctorb.GetILGenerator().Emit(OpCodes.Ret); // Parameters @@ -156,7 +156,7 @@ public void AssemblyWithDifferentTypes() var ctorb2 = tb5.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.RTSpecialName, CallingConventions.Standard, [typeof(int), typeof(object)]); ctorb2.GetILGenerator().Emit(OpCodes.Ret); // method - var mb = tb5.DefineMethod("Method1", MethodAttributes.Public, CallingConventions.Standard, typeof(int), cmodsReq1, cmodsOpt1, [typeof(int), typeof(object)], new Type[][] { cmodsReq1, null }, new Type[][] { cmodsOpt1, null }); + var mb = tb5.DefineMethod("Method1", MethodAttributes.Public, CallingConventions.Standard, typeof(int), cmodsReq1, cmodsOpt1, [typeof(int), typeof(object)], [cmodsReq1, null], [cmodsOpt1, null]); mb.SetImplementationFlags(MethodImplAttributes.NoInlining); mb.GetILGenerator().Emit(OpCodes.Ldc_I4_0); mb.GetILGenerator().Emit(OpCodes.Ret); @@ -322,14 +322,14 @@ void CheckAssembly(Assembly a) Assert.Equal(42, field.GetRawConstantValue()); field = type4.GetField("FieldOffset"); Assert.NotNull(field); - /* TODO: Not supported yet + field = type4.GetField("FieldModopt"); var cmods = field.GetRequiredCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(int), cmods[0]); + Assert.Equal(typeof(int).FullName, cmods[0].FullName); cmods = field.GetOptionalCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(uint), cmods[0]);*/ + Assert.Equal(typeof(uint).FullName, cmods[0].FullName); // Simple marshal field = type4.GetField("FieldMarshal1"); CheckMarshallAttribute(field.GetCustomAttributesData(), UnmanagedType.U4); @@ -347,11 +347,11 @@ void CheckAssembly(Assembly a) field = type4.GetField("FieldCAttr"); CheckCattr(field.GetCustomAttributesData()); - // TODO: Global fields - /*field = a.ManifestModule.GetField("Data1"); + // Global fields + field = a.ManifestModule.GetField("Data1"); Assert.NotNull(field); field = a.ManifestModule.GetField("Data2"); - Assert.NotNull(field);*/ + Assert.NotNull(field); // Methods and signatures var typeMethods = a.GetType("TypeMethods"); @@ -367,13 +367,12 @@ void CheckAssembly(Assembly a) Assert.Equal(typeof(int).FullName, parameters[0].ParameterType.FullName); Assert.Equal(16, parameters[0].RawDefaultValue); CheckCattr(parameters[0].GetCustomAttributesData()); - /* TODO: Not supported yet cmods = parameters[0].GetRequiredCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(object), cmods[0]); + Assert.Equal(typeof(object).FullName, cmods[0].FullName); cmods = parameters[0].GetOptionalCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(int), cmods[0]);*/ + Assert.Equal(typeof(int).FullName, cmods[0].FullName); Assert.Equal("param2", parameters[1].Name); Assert.Equal(ParameterAttributes.Out | ParameterAttributes.HasFieldMarshal, parameters[1].Attributes); Assert.Equal(typeof(object).FullName, parameters[1].ParameterType.FullName); @@ -412,12 +411,12 @@ void CheckAssembly(Assembly a) // return type var rparam = method.ReturnParameter; - /*cmods = rparam.GetRequiredCustomModifiers(); + cmods = rparam.GetRequiredCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(object), cmods[0]); + Assert.Equal(typeof(object).FullName, cmods[0].FullName); cmods = rparam.GetOptionalCustomModifiers(); Assert.Equal(1, cmods.Length); - Assert.Equal(typeof(int), cmods[0]);*/ + Assert.Equal(typeof(int).FullName, cmods[0].FullName); CheckMarshallAttribute(rparam.GetCustomAttributesData(), UnmanagedType.U4); CheckCattr(rparam.GetCustomAttributesData()); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs index 7b7a15bde56854..31ec0e31d87be0 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using Xunit; namespace System.Reflection.Emit.Tests diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs index e838af252c31f7..8ab0de66a8f205 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs @@ -80,7 +80,7 @@ public void DefineLiteral(Type underlyingType, object literalValue) FieldInfo testField = testEnum.GetField("FieldOne"); Assert.Equal(enumBuilder.Name, testField.DeclaringType.Name); - Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal, literal.Attributes); + Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault, literal.Attributes); Assert.Equal(enumBuilder.AsType().FullName, testField.FieldType.FullName); } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index f11c899eb30406..331f8169f3afa5 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Reflection.Emit.Tests @@ -192,7 +191,7 @@ public void ILMaxStack_Test() using (TempFile file = TempFile.Create()) { AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), new [] { typeof(int), typeof(long), typeof(short), typeof(byte) }); + MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), new[] { typeof(int), typeof(long), typeof(short), typeof(byte) }); ILGenerator il1 = method1.GetILGenerator(); // public int Method1(int x, int y, short z, byte r) => @@ -711,7 +710,7 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() FieldBuilder myField = type.DefineField("Field", typeParams[0], FieldAttributes.Public); MethodBuilder genericMethod = type.DefineMethod("GM", MethodAttributes.Public | MethodAttributes.Static); GenericTypeParameterBuilder[] methodParams = genericMethod.DefineGenericParameters("U"); - genericMethod.SetSignature(null, null, null, new [] { methodParams[0] }, null, null); + genericMethod.SetSignature(null, null, null, new[] { methodParams[0] }, null, null); ILGenerator ilg = genericMethod.GetILGenerator(); Type SampleOfU = type.MakeGenericType(methodParams[0]); ilg.DeclareLocal(SampleOfU); @@ -742,22 +741,22 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() /* Generated IL would like this in C#: public class MyType { - public T Field; - - public static void GM(U P_0) - { - MyType myType = new MyType(); - myType.Field = P_0; - Console.WriteLine(myType.Field); - } + public T Field; + + public static void GM(U P_0) + { + MyType myType = new MyType(); + myType.Field = P_0; + Console.WriteLine(myType.Field); + } } internal class Dummy { - public static void Main() - { - MyType.GM("HelloWorld"); - } + public static void Main() + { + MyType.GM("HelloWorld"); + } } */ saveMethod.Invoke(ab, new[] { file.Path }); @@ -960,7 +959,7 @@ public void MemberReferenceExceptions() Assert.Throws("cls", () => il.Emit(OpCodes.Switch, nullType)); Assert.Throws("methodInfo", () => il.EmitCall(OpCodes.Call, nullMethod, null)); // only OpCodes.Switch expected - Assert.Throws("opcode", () => il.Emit(OpCodes.Call, new Label[0])); + Assert.Throws("opcode", () => il.Emit(OpCodes.Call, new Label[0])); // only OpCodes.Call or .OpCodes.Callvirt or OpCodes.Newob expected Assert.Throws("opcode", () => il.Emit(OpCodes.Switch, typeof(object).GetConstructor(Type.EmptyTypes))); // Undefined label @@ -1177,7 +1176,7 @@ public void TryCatchFilterCatchBlock() ilGenerator.Emit(OpCodes.Ldarg_1); ilGenerator.Emit(OpCodes.Div); ilGenerator.Emit(OpCodes.Stloc_0); - Assert.Equal(2, maxStackField.GetValue(ilGenerator)); + Assert.Equal(2, maxStackField.GetValue(ilGenerator)); ilGenerator.BeginCatchBlock(overflowException); ilGenerator.EmitWriteLine("Overflow Exception!"); ilGenerator.ThrowException(overflowException); @@ -1314,9 +1313,9 @@ public void TryFilterCatchFinallyBlock() AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int), typeof(int) }); Type overflowEType = typeof(OverflowException); - ConstructorInfo myConstructorInfo = overflowEType.GetConstructor(new [] { typeof(string) }); + ConstructorInfo myConstructorInfo = overflowEType.GetConstructor(new[] { typeof(string) }); MethodInfo myExToStrMI = overflowEType.GetMethod("ToString"); - MethodInfo myWriteLineMI = typeof(Console).GetMethod("WriteLine", new [] {typeof(string),typeof(object) }); + MethodInfo myWriteLineMI = typeof(Console).GetMethod("WriteLine", new[] { typeof(string), typeof(object) }); ILGenerator ilGenerator = method.GetILGenerator(); LocalBuilder myLocalBuilder1 = ilGenerator.DeclareLocal(typeof(int)); LocalBuilder myLocalBuilder2 = ilGenerator.DeclareLocal(overflowEType); @@ -1592,75 +1591,71 @@ public void DeeperNestedTryCatchFilterFinallyBlocks() private static int Int32Sum(int a, int b) => a + b; - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void EmitCalliBlittable() { - RemoteExecutor.Invoke(() => + int a = 1, b = 1, result = 2; + using (TempFile file = TempFile.Create()) { - int a = 1, b = 1, result = 2; - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliBlittable"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - Type returnType = typeof(int); - MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); - methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldarg_0); - il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, [typeof(int), typeof(int)]); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - var del = new Int32SumStdCall(Int32Sum); - IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); - object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); - GC.KeepAlive(del); - - Assert.IsType(returnType, resultValue); - Assert.Equal(result, resultValue); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliBlittable"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + Type returnType = typeof(int); + MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); + methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldarg_0); + il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, [typeof(int), typeof(int)]); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + var del = new Int32SumStdCall(Int32Sum); + IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); + object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); + GC.KeepAlive(del); + + Assert.IsType(returnType, resultValue); + Assert.Equal(result, resultValue); + tlc.Unload(); + } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void EmitCalliManagedBlittable() { - RemoteExecutor.Invoke(() => + int a = 1, b = 1, result = 2; + using (TempFile file = TempFile.Create()) { - int a = 1, b = 1, result = 2; - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliManagedBlittable"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - Type returnType = typeof(int); - MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); - methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); - MethodInfo method = typeof(AssemblySaveILGeneratorTests).GetMethod(nameof(Int32Sum), BindingFlags.NonPublic | BindingFlags.Static)!; - IntPtr funcPtr = method.MethodHandle.GetFunctionPointer(); - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldarg_0); - il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, returnType, [typeof(int), typeof(int)], null); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); - - Assert.IsType(returnType, resultValue); - Assert.Equal(result, resultValue); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliManagedBlittable"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + Type returnType = typeof(int); + MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); + methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); + MethodInfo method = typeof(AssemblySaveILGeneratorTests).GetMethod(nameof(Int32Sum), BindingFlags.NonPublic | BindingFlags.Static)!; + IntPtr funcPtr = method.MethodHandle.GetFunctionPointer(); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Ldarg_0); + il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, returnType, [typeof(int), typeof(int)], null); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, a, b]); + + Assert.IsType(returnType, resultValue); + Assert.Equal(result, resultValue); + tlc.Unload(); + } } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -1668,39 +1663,37 @@ public void EmitCalliManagedBlittable() private static string StringReverse(string a) => string.Join("", a.Reverse()); - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void EmitCalliNonBlittable() { - RemoteExecutor.Invoke(() => + string input = "Test string!", result = "!gnirts tseT"; + using (TempFile file = TempFile.Create()) { - string input = "Test string!", result = "!gnirts tseT"; - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliNonBlittable"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - Type returnType = typeof(string); - MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(string)]); - methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldarg_0); - il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, [typeof(string)]); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - var del = new StringReverseCdecl(StringReverse); - IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); - object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, input]); - GC.KeepAlive(del); - - Assert.IsType(returnType, resultValue); - Assert.Equal(result, resultValue); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliNonBlittable"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + Type returnType = typeof(string); + MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(string)]); + methodBuilder.SetImplementationFlags(MethodImplAttributes.NoInlining); + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldarg_0); + il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, [typeof(string)]); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + var del = new StringReverseCdecl(StringReverse); + IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(del); + object resultValue = typeFromDisk.GetMethod("F", BindingFlags.Public | BindingFlags.Static).Invoke(null, [funcPtr, input]); + GC.KeepAlive(del); + + Assert.IsType(returnType, resultValue); + Assert.Equal(result, resultValue); + tlc.Unload(); + } } [Fact] @@ -2071,7 +2064,7 @@ static void GetCode() MethodInfo getMaxStackMethod = GetMaxStackMethod(); // Observed depth of 2, with "adjustment" of 1. - Assert.Equal(2 +1, getMaxStackMethod.Invoke(ilg, null)); + Assert.Equal(2 + 1, getMaxStackMethod.Invoke(ilg, null)); } } @@ -2106,131 +2099,124 @@ static void GetCode() } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void SimpleForLoopTest() { - RemoteExecutor.Invoke(() => + using (TempFile file = TempFile.Create()) { - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder mb2 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); - ILGenerator il = mb2.GetILGenerator(); - LocalBuilder sum = il.DeclareLocal(typeof(int)); - LocalBuilder i = il.DeclareLocal(typeof(int)); - Label loopEnd = il.DefineLabel(); - Label loopStart = il.DefineLabel(); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Stloc_1); - il.MarkLabel(loopStart); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Bgt, loopEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Br, loopStart); - il.MarkLabel(loopEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - MethodInfo getMaxStackMethod = GetMaxStackMethod(); - Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - MethodInfo sumMethodFromDisk = typeFromDisk.GetMethod("SumMethod"); - Assert.Equal(55, sumMethodFromDisk.Invoke(null, [10])); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder mb2 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); + ILGenerator il = mb2.GetILGenerator(); + LocalBuilder sum = il.DeclareLocal(typeof(int)); + LocalBuilder i = il.DeclareLocal(typeof(int)); + Label loopEnd = il.DefineLabel(); + Label loopStart = il.DefineLabel(); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Stloc_1); + il.MarkLabel(loopStart); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Bgt, loopEnd); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Br, loopStart); + il.MarkLabel(loopEnd); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + MethodInfo getMaxStackMethod = GetMaxStackMethod(); + Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + MethodInfo sumMethodFromDisk = typeFromDisk.GetMethod("SumMethod"); + Assert.Equal(55, sumMethodFromDisk.Invoke(null, [10])); + tlc.Unload(); + } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void RecursiveSumTest() { - RemoteExecutor.Invoke(() => + using (TempFile file = TempFile.Create()) { - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("RecursiveSumTest"), out MethodInfo saveMethod); - TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); - MethodBuilder mb2 = tb.DefineMethod("RecursiveMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); - ILGenerator il = mb2.GetILGenerator(); - Label loopEnd = il.DefineLabel(); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ble, loopEnd); // if (value1 <= value2) - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Sub); - il.Emit(OpCodes.Call, mb2); - il.Emit(OpCodes.Add); - il.Emit(OpCodes.Ret); - il.MarkLabel(loopEnd); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ret); - tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - MethodInfo getMaxStackMethod = GetMaxStackMethod(); - Assert.Equal(3, getMaxStackMethod.Invoke(il, null)); - - Assembly assemblyFromDisk = Assembly.LoadFrom(file.Path); - Type typeFromDisk = assemblyFromDisk.GetType("MyType"); - MethodInfo recursiveMethodFromDisk = typeFromDisk.GetMethod("RecursiveMethod"); - Assert.NotNull(recursiveMethodFromDisk); - Assert.Equal(55, recursiveMethodFromDisk.Invoke(null, [10])); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder mb2 = tb.DefineMethod("RecursiveMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); + ILGenerator il = mb2.GetILGenerator(); + Label loopEnd = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ble, loopEnd); // if (value1 <= value2) + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Sub); + il.Emit(OpCodes.Call, mb2); + il.Emit(OpCodes.Add); + il.Emit(OpCodes.Ret); + il.MarkLabel(loopEnd); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ret); + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + MethodInfo getMaxStackMethod = GetMaxStackMethod(); + Assert.Equal(3, getMaxStackMethod.Invoke(il, null)); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + MethodInfo recursiveMethodFromDisk = typeFromDisk.GetMethod("RecursiveMethod"); + Assert.NotNull(recursiveMethodFromDisk); + Assert.Equal(55, recursiveMethodFromDisk.Invoke(null, [10])); + tlc.Unload(); + } } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void CallOpenGenericMembersFromConstructedGenericType() { - RemoteExecutor.Invoke(() => + using (TempFile file = TempFile.Create()) { - using (TempFile file = TempFile.Create()) - { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder method = type.DefineMethod("M1", MethodAttributes.Public, typeof(string), null); - - ILGenerator ilGenerator = method.GetILGenerator(); - LocalBuilder span = ilGenerator.DeclareLocal(typeof(ReadOnlySpan)); - LocalBuilder str = ilGenerator.DeclareLocal(typeof(string)); - - ilGenerator.Emit(OpCodes.Ldstr, "hello"); - ilGenerator.Emit(OpCodes.Call, typeof(MemoryExtensions).GetMethod("AsSpan", [typeof(string)])!); - ilGenerator.Emit(OpCodes.Stloc, span); - ilGenerator.Emit(OpCodes.Ldloca_S, span); - ilGenerator.Emit(OpCodes.Ldc_I4_1); - ilGenerator.Emit(OpCodes.Call, typeof(ReadOnlySpan).GetMethod("Slice", [typeof(int)])!); - ilGenerator.Emit(OpCodes.Stloc, span); - ilGenerator.Emit(OpCodes.Ldloca_S, span); - ilGenerator.Emit(OpCodes.Constrained, typeof(ReadOnlySpan)); - ilGenerator.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); - ilGenerator.Emit(OpCodes.Ret); - - type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Type typeFromDisk = Assembly.LoadFile(file.Path).GetType("MyType"); - string result = (string)typeFromDisk.GetMethod("M1").Invoke(Activator.CreateInstance(typeFromDisk), null); - Assert.Equal("ello", result); - } - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + MethodBuilder method = type.DefineMethod("M1", MethodAttributes.Public, typeof(string), null); + + ILGenerator ilGenerator = method.GetILGenerator(); + LocalBuilder span = ilGenerator.DeclareLocal(typeof(ReadOnlySpan)); + LocalBuilder str = ilGenerator.DeclareLocal(typeof(string)); + + ilGenerator.Emit(OpCodes.Ldstr, "hello"); + ilGenerator.Emit(OpCodes.Call, typeof(MemoryExtensions).GetMethod("AsSpan", [typeof(string)])!); + ilGenerator.Emit(OpCodes.Stloc, span); + ilGenerator.Emit(OpCodes.Ldloca_S, span); + ilGenerator.Emit(OpCodes.Ldc_I4_1); + ilGenerator.Emit(OpCodes.Call, typeof(ReadOnlySpan).GetMethod("Slice", [typeof(int)])!); + ilGenerator.Emit(OpCodes.Stloc, span); + ilGenerator.Emit(OpCodes.Ldloca_S, span); + ilGenerator.Emit(OpCodes.Constrained, typeof(ReadOnlySpan)); + ilGenerator.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString")); + ilGenerator.Emit(OpCodes.Ret); + + type.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + string result = (string)typeFromDisk.GetMethod("M1").Invoke(Activator.CreateInstance(typeFromDisk), null); + Assert.Equal("ello", result); + tlc.Unload(); + } } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs new file mode 100644 index 00000000000000..6a3bb1e4dcbcbc --- /dev/null +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs @@ -0,0 +1,291 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Reflection.Emit.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + public class AssemblySaveModuleBuilderTests + { + [Fact] + public void DefineGlobalMethodAndCreateGlobalFunctionsTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + MethodBuilder method = module.DefineGlobalMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public, null, null); + ILGenerator ilGenerator = method.GetILGenerator(); + ilGenerator.EmitWriteLine("Hello World from global method."); + ilGenerator.Emit(OpCodes.Ret); + + MethodBuilder method2 = module.DefineGlobalMethod("MyMethod", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(int), [typeof(string), typeof(int)]); + ilGenerator = method2.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", [typeof(string)])); + ilGenerator.Emit(OpCodes.Ldarg_1); + ilGenerator.Emit(OpCodes.Ret); + + module.CreateGlobalFunctions(); + Assert.Null(method.DeclaringType); + Assert.Equal(typeof(void), method.ReturnType); + Assert.Equal(method, module.GetMethod("TestMethod")); + Assert.Equal(method2, module.GetMethod("MyMethod", [typeof(string), typeof(int)])); + Assert.Equal(2, module.GetMethods().Length); + + saveMethod.Invoke(ab, [file.Path]); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + MethodInfo globalMethod1 = assemblyFromDisk.ManifestModule.GetMethod("TestMethod"); + Assert.NotNull(globalMethod1); + Assert.True(globalMethod1.IsStatic); + Assert.True(globalMethod1.IsPublic); + Assert.Equal(0, globalMethod1.GetParameters().Length); + Assert.Equal(method.ReturnType.FullName, globalMethod1.ReturnType.FullName); + Assert.NotNull(globalMethod1.DeclaringType); + + MethodInfo globalMethod2 = assemblyFromDisk.ManifestModule.GetMethod("MyMethod"); + Assert.NotNull(globalMethod2); + Assert.True(globalMethod2.IsStatic); + Assert.True(globalMethod2.IsPublic); + Assert.Equal(2, globalMethod2.GetParameters().Length); + Assert.Equal(method.ReturnType.FullName, globalMethod1.ReturnType.FullName); + Assert.Equal("", globalMethod2.DeclaringType.Name); + } + } + } + + [Fact] + public void DefineGlobalMethodAndCreateGlobalFunctions_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + Assert.Throws(() => module.DefineGlobalMethod("TestMethod", MethodAttributes.Public, null, null)); // must be static + MethodBuilder method = module.DefineGlobalMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public, null, null); + ILGenerator ilGenerator = method.GetILGenerator(); + ilGenerator.EmitWriteLine("Hello World from global method."); + ilGenerator.Emit(OpCodes.Ret); + + Assert.Throws(() => module.GetMethod("TestMethod")); // not supported when not created + Assert.Throws(() => module.GetMethods()); + module.CreateGlobalFunctions(); + + Assert.Null(method.DeclaringType); + Assert.Throws(() => module.CreateGlobalFunctions()); // already created + Assert.Throws(() => module.DefineGlobalMethod("TestMethod2", MethodAttributes.Static | MethodAttributes.Public, null, null)); + } + + [Fact] + public static void DefinePInvokeMethodTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + DpmParams p = new DpmParams() { MethodName = "A2", LibName = "Foo2.dll", EntrypointName = "Wha2", ReturnType = typeof(int), + ParameterTypes = [typeof(int)], NativeCallConv = CallingConvention.Cdecl }; + + ModuleBuilder modb = ab.DefineDynamicModule("MyModule"); + MethodBuilder mb = modb.DefinePInvokeMethod(p.MethodName, p.LibName, p.EntrypointName, p.Attributes, p.ManagedCallConv, p.ReturnType, + p.ParameterTypes, p.NativeCallConv, p.Charset); + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); + + modb.CreateGlobalFunctions(); + saveMethod.Invoke(ab, [file.Path]); + MethodInfo m = modb.GetMethod(p.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.Any, p.ParameterTypes, null); + Assert.NotNull(m); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Module moduleFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetModule("MyModule"); + Assert.Equal(1, moduleFromDisk.GetMethods().Length); + MethodInfo method = moduleFromDisk.GetMethod(p.MethodName); + Assert.NotNull(method); + AssemblySaveTypeBuilderAPIsTests.VerifyPInvokeMethod(method.DeclaringType, method, p, mlc.CoreAssembly); + } + } + } + + [Theory] + [InlineData(FieldAttributes.Static | FieldAttributes.Public)] + [InlineData(FieldAttributes.Private)] + [InlineData(FieldAttributes.FamANDAssem)] + [InlineData(FieldAttributes.Assembly | FieldAttributes.SpecialName)] + public void DefineUninitializedDataTest(FieldAttributes attributes) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + foreach (int size in new int[] { 1, 2, 0x003f0000 - 1 }) + { + FieldBuilder field = module.DefineUninitializedData(size.ToString(), size, attributes); + + Assert.Equal(size.ToString(), field.Name); + Assert.True(field.IsStatic); + Assert.True((field.Attributes & FieldAttributes.HasFieldRVA) != 0); + Assert.Equal(attributes | FieldAttributes.Static | FieldAttributes.HasFieldRVA, field.Attributes); + } + } + + [Fact] + public void DefineUninitializedData_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + + AssertExtensions.Throws("name", () => module.DefineUninitializedData(null, 1, FieldAttributes.Family)); + AssertExtensions.Throws("name", () => module.DefineUninitializedData("", 1, FieldAttributes.Public)); + + foreach (int size in new int[] { -1, 0, 0x003f0000, 0x003f0000 + 1 }) + { + AssertExtensions.Throws("size", () => module.DefineUninitializedData("TestField", size, FieldAttributes.Private)); + } + + module.CreateGlobalFunctions(); + Assert.Throws(() => module.DefineUninitializedData("TestField", 1, FieldAttributes.HasDefault)); + } + + [Theory] + [InlineData(FieldAttributes.Static | FieldAttributes.Public)] + [InlineData(FieldAttributes.Static | FieldAttributes.Private)] + [InlineData(FieldAttributes.Private)] + public void DefineInitializedDataTest(FieldAttributes attributes) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + FieldBuilder field = module.DefineInitializedData("MyField", [01, 00, 01], attributes); + + Assert.True(field.IsStatic); + Assert.Equal((attributes & FieldAttributes.Public) != 0, field.IsPublic); + Assert.Equal((attributes & FieldAttributes.Private) != 0, field.IsPrivate); + Assert.Equal("MyField", field.Name); + } + + [Fact] + public void DefineInitializedData_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + + AssertExtensions.Throws("name", () => module.DefineInitializedData(null, [1, 0, 1], FieldAttributes.Public)); + AssertExtensions.Throws("name", () => module.DefineInitializedData("", [1, 0, 1], FieldAttributes.Private)); + AssertExtensions.Throws("data", () => module.DefineInitializedData("MyField", null, FieldAttributes.Public)); + AssertExtensions.Throws("Length", () => module.DefineInitializedData("MyField", [], FieldAttributes.Public)); + AssertExtensions.Throws("Length", () => module.DefineInitializedData("MyField", new byte[0x3f0000], FieldAttributes.Public)); + + FieldBuilder field = module.DefineInitializedData("MyField", [1, 0, 1], FieldAttributes.Public); + module.CreateGlobalFunctions(); + + Assert.Null(field.DeclaringType); + Assert.Throws(() => module.DefineInitializedData("MyField2", new byte[] { 1, 0, 1 }, FieldAttributes.Public)); + } + + [Fact] + // Field RVA alignment is not working on mono + [ActiveIssue("https://github.com/dotnet/runtime/issues/97172", TestRuntimes.Mono)] + public void DefineInitializedData_EnsureAlignmentIsMinimumNeededForUseOfCreateSpan() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + TypeBuilder tb = module.DefineType("MyType", TypeAttributes.Public); + // Create static field data in a variety of orders that requires the runtime to actively apply alignment + // RuntimeHelpers.CreateSpan requires data to be naturally aligned within the "PE" file. At this time CreateSpan only + // requires alignments up to 8 bytes. + FieldBuilder field1Byte = module.DefineInitializedData("Field1Byte", new byte[] { 1 }, FieldAttributes.Public); + byte[] field4Byte_1_data = new byte[] { 1, 2, 3, 4 }; + byte[] field8Byte_1_data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + byte[] field4Byte_2_data = new byte[] { 5, 6, 7, 8 }; + byte[] field8Byte_2_data = new byte[] { 9, 10, 11, 12, 13, 14, 15, 16 }; + FieldBuilder field4Byte_1 = module.DefineInitializedData("Field4Bytes_1", field4Byte_1_data, FieldAttributes.Public); + FieldBuilder tbField4Byte_1 = tb.DefineInitializedData("Field4Bytes_1", field4Byte_1_data, FieldAttributes.Public); + FieldBuilder field8Byte_1 = module.DefineInitializedData("Field8Bytes_1", field8Byte_1_data, FieldAttributes.Public); + FieldBuilder field4Byte_2 = module.DefineInitializedData("Field4Bytes_2", field4Byte_2_data, FieldAttributes.Public); + FieldBuilder field8Byte_2 = module.DefineInitializedData("Field8Bytes_2", field8Byte_2_data, FieldAttributes.Public); + FieldBuilder tbField8Byte_2 = tb.DefineInitializedData("Field8Bytes_2", field8Byte_2_data, FieldAttributes.Public); + module.CreateGlobalFunctions(); + tb.CreateType(); + Assert.Null(field4Byte_1.DeclaringType); + Assert.Null(field8Byte_1.DeclaringType); + Assert.Null(field4Byte_2.DeclaringType); + Assert.Null(field8Byte_2.DeclaringType); + + TypeBuilder checkTypeBuilder = module.DefineType("CheckType", TypeAttributes.Public); + CreateLoadAddressMethod("LoadAddress1", field1Byte); + CreateLoadAddressMethod("LoadAddress4_1", field4Byte_1); + CreateLoadAddressMethod("LoadAddress4_3", tbField4Byte_1); + CreateLoadAddressMethod("LoadAddress4_2", field4Byte_2); + CreateLoadAddressMethod("LoadAddress8_1", field8Byte_1); + CreateLoadAddressMethod("LoadAddress8_2", field8Byte_2); + CreateLoadAddressMethod("LoadAddress8_3", tbField8Byte_2); + + void CreateLoadAddressMethod(string name, FieldBuilder fieldBuilder) + { + MethodBuilder loadAddressMethod = checkTypeBuilder.DefineMethod(name, MethodAttributes.Public | MethodAttributes.Static, typeof(IntPtr), null); + ILGenerator methodIL = loadAddressMethod.GetILGenerator(); + methodIL.Emit(OpCodes.Ldsflda, fieldBuilder); + methodIL.Emit(OpCodes.Ret); + } + + checkTypeBuilder.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type checkType = assemblyFromDisk.GetType("CheckType"); + + CheckMethod("LoadAddress4_1", 4, field4Byte_1_data); + CheckMethod("LoadAddress4_3", 4, field4Byte_1_data); + CheckMethod("LoadAddress4_2", 4, field4Byte_2_data); + CheckMethod("LoadAddress8_1", 8, field8Byte_1_data); + CheckMethod("LoadAddress8_2", 8, field8Byte_2_data); + CheckMethod("LoadAddress8_3", 8, field8Byte_2_data); + tlc.Unload(); + + void CheckMethod(string name, int minAlignmentRequired, byte[] dataToVerify) + { + MethodInfo methodToCall = checkType.GetMethod(name); + nint address = (nint)methodToCall.Invoke(null, null); + + for (int i = 0; i < dataToVerify.Length; i++) + { + Assert.Equal(dataToVerify[i], Marshal.ReadByte(address + (nint)i)); + } + Assert.Equal(name + "_0" + "_" + address.ToString(), name + "_" + (address % minAlignmentRequired).ToString() + "_" + address.ToString()); + } + } + } + + [Fact] + // Standalone signature is not supported on mono + [ActiveIssue("https://github.com/dotnet/runtime/issues/96389", TestRuntimes.Mono)] + public void GetABCMetadataToken_Validations() + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + ModuleBuilder module = ab.DefineDynamicModule("MyModule"); + TypeBuilder type = module.DefineType("MyType", TypeAttributes.Public); + MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public); + FieldBuilder field = type.DefineField("MyField", typeof(int), FieldAttributes.Public); + ConstructorBuilder constructor = type.DefineDefaultConstructor(MethodAttributes.Public); + + Assert.Throws(() => module.GetMethodMetadataToken(method)); + Assert.Throws(() => module.GetMethodMetadataToken(constructor)); + Assert.Throws(() => module.GetFieldMetadataToken(field)); + Assert.Throws(() => module.GetTypeMetadataToken(type)); + + SignatureHelper signature = SignatureHelper.GetMethodSigHelper(CallingConventions.HasThis, typeof(void)); + signature.AddArgument(typeof(string)); + signature.AddArgument(typeof(int)); + + int signatureToken = module.GetSignatureMetadataToken(signature); + int stringToken = module.GetStringMetadataToken("Hello"); + + Assert.True(signatureToken > 0); + Assert.True(stringToken > 0); + } + } +} diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs index 69ec92d4b97a51..65762a54b4751b 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs @@ -4,10 +4,24 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.Loader; using Xunit; namespace System.Reflection.Emit.Tests { + class TestAssemblyLoadContext : AssemblyLoadContext + { + public TestAssemblyLoadContext() : base(isCollectible: true) + { + } + + protected override Assembly? Load(AssemblyName name) + { + return null; + } + } + + internal static class AssemblySaveTools { private static readonly AssemblyName s_assemblyName = new AssemblyName("MyDynamicAssembly") diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveMethodBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs similarity index 59% rename from src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveMethodBuilderTests.cs rename to src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index 5d1b92e0cf52d4..88755b56714897 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveMethodBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -1,14 +1,16 @@ // 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.IO; using System.Runtime.InteropServices; +using System.Text; using Xunit; namespace System.Reflection.Emit.Tests { [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] - public class AssemblySaveMethodBuilderTests + public class AssemblySaveTypeBuilderAPIsTests { [Fact] public void DefineMethodOverride_InterfaceMethod() @@ -315,6 +317,17 @@ public abstract class PartialImplementation : InterfaceDerivedFromOtherInterface public abstract string M2(int a); } + public interface IDefaultImplementation + { + void Method() => Console.WriteLine("Hello"); + } + + public interface IStaticAbstract + { + static abstract void Method(); + } + + [Fact] public void CreateType_ValidateAllAbstractMethodsAreImplemented() { @@ -329,11 +342,18 @@ public void CreateType_ValidateAllAbstractMethodsAreImplemented() baseTypeImplementedTheInterfaceMethod.DefineMethod("M2", MethodAttributes.Public, typeof(string), [typeof(int)]).GetILGenerator().Emit(OpCodes.Ret); TypeBuilder baseTypePartiallyImplemented = module.DefineType("Type4", TypeAttributes.Public, parent: typeof(PartialImplementation)); baseTypePartiallyImplemented.AddInterfaceImplementation(typeof(InterfaceDerivedFromOtherInterface)); + TypeBuilder interfaceHasStaticAbstractMethod = module.DefineType("Type5", TypeAttributes.Public); + interfaceHasStaticAbstractMethod.AddInterfaceImplementation(typeof(IStaticAbstract)); + TypeBuilder interfaceMethodHasDefaultImplementation = module.DefineType("Type6", TypeAttributes.Public); + interfaceMethodHasDefaultImplementation.AddInterfaceImplementation(typeof(IDefaultImplementation)); Assert.Throws(() => typeNotImplementedIfaceMethod.CreateType()); Assert.Throws(() => partiallyImplementedType.CreateType()); baseTypeImplementedTheInterfaceMethod.CreateType(); // succeeds + interfaceMethodHasDefaultImplementation.CreateType(); //succeeds Assert.Throws(() => baseTypePartiallyImplemented.CreateType()); + Assert.Throws(() => interfaceHasStaticAbstractMethod.CreateType()); + Assert.Throws(() => interfaceMethodHasDefaultImplementation.DefineTypeInitializer()); } [Fact] @@ -395,5 +415,312 @@ public void GetMethodsGetMethodImpl_Tests() Assert.Throws(() => type.GetMethod("VoidMethod")); Assert.Throws(() => type.GetMethod("VoidMethod", BindingFlags.NonPublic | BindingFlags.Instance)); } + + [Fact] + public void ReturnTypeAndParameterRequiredOptionalCustomModifiers() + { + using (TempFile file = TempFile.Create()) + { + Type[] cmodsReq1 = [typeof(object), typeof(string)]; + Type[] cmodsReq2 = [typeof(uint)]; + Type[] cmodsOpt1 = [typeof(int)]; + Type[] cmodsOpt2 = [typeof(long), typeof(byte), typeof(bool)]; + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + MethodBuilder methodAll = type.DefineMethod("AllModifiers", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, + typeof(string), [typeof(int), typeof(short)], [typeof(Version)], [typeof(int), typeof(long)], [cmodsReq1, cmodsReq2], [cmodsOpt1, cmodsOpt2]); + ILGenerator ilGenerator = methodAll.GetILGenerator(); + ilGenerator.Emit(OpCodes.Ldstr, "Hello World"); + ilGenerator.Emit(OpCodes.Ret); + Type createdType = type.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo allModMethod = typeFromDisk.GetMethod("AllModifiers"); + Type[] returnReqMods = allModMethod.ReturnParameter.GetRequiredCustomModifiers(); + Type[] returnOptMods = allModMethod.ReturnParameter.GetOptionalCustomModifiers(); + Type[] par0RequiredMods = allModMethod.GetParameters()[0].GetRequiredCustomModifiers(); + Type[] par0OptionalMods = allModMethod.GetParameters()[0].GetOptionalCustomModifiers(); + Assert.Equal(2, returnReqMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(short).FullName), returnReqMods[0]); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(int).FullName), returnReqMods[1]); + Assert.Equal(1, returnOptMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(typeof(Version).FullName), returnOptMods[0]); + Assert.Equal(cmodsReq1.Length, par0RequiredMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[1].FullName), par0RequiredMods[0]); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsReq1[0].FullName), par0RequiredMods[1]); + Assert.Equal(cmodsOpt1.Length, par0OptionalMods.Length); + Assert.Equal(mlc.CoreAssembly.GetType(cmodsOpt1[0].FullName), par0OptionalMods[0]); + Assert.Equal(cmodsReq2.Length, allModMethod.GetParameters()[1].GetRequiredCustomModifiers().Length); + Assert.Equal(cmodsOpt2.Length, allModMethod.GetParameters()[1].GetOptionalCustomModifiers().Length); + } + } + } + + [PlatformSpecific(TestPlatforms.Windows)] + [Fact] + public static void DefinePInvokeMethodExecution_Windows() + { + const string EnvironmentVariable = "COMPUTERNAME"; + + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("DefinePInvokeMethodExecution_Windows"), out MethodInfo saveMethod); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); + MethodBuilder mb = tb.DefinePInvokeMethod( + "GetEnvironmentVariableW", + "kernel32.dll", + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl, + CallingConventions.Standard, + typeof(int), + [typeof(string), typeof(StringBuilder), typeof(int)], + CallingConvention.StdCall, + CharSet.Unicode); + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); + + Type t = tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("GetEnvironmentVariableW", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(methodFromDisk); + + string expected = Environment.GetEnvironmentVariable(EnvironmentVariable); + + int numCharsRequired = (int)methodFromDisk.Invoke(null, [EnvironmentVariable, null, 0]); + if (numCharsRequired == 0) + { + // Environment variable is not defined. Make sure we got that result using both techniques. + Assert.Null(expected); + } + else + { + StringBuilder sb = new StringBuilder(numCharsRequired); + int numCharsWritten = (int)methodFromDisk.Invoke(null, [EnvironmentVariable, sb, numCharsRequired]); + Assert.NotEqual(0, numCharsWritten); + string actual = sb.ToString(); + Assert.Equal(expected, actual); + } + tlc.Unload(); + } + } + + public static IEnumerable TestData + { + get + { + yield return [new DpmParams() { MethodName = "A1", LibName = "Foo1.dll", EntrypointName = "A1", + ReturnType = typeof(int), ParameterTypes = [typeof(string)] }]; + yield return [new DpmParams() { MethodName = "A2", LibName = "Foo2.dll", EntrypointName = "Wha2", + ReturnType = typeof(int), ParameterTypes = [typeof(int)], + NativeCallConv = CallingConvention.Cdecl}]; + yield return [new DpmParams() { MethodName = "A3", LibName = "Foo3.dll", EntrypointName = "Wha3", + ReturnType = typeof(double), ParameterTypes = [typeof(string)], + Charset = CharSet.Ansi, ReturnTypeOptMods = [typeof(short)]}]; + yield return [new DpmParams() { MethodName = "A4", LibName = "Foo4.dll", EntrypointName = "Wha4", + ReturnType = typeof(IntPtr), ParameterTypes = [typeof(string)], + Charset = CharSet.Auto, ReturnTypeReqMods = [typeof(bool)], NativeCallConv = CallingConvention.FastCall}]; + yield return [new DpmParams() { MethodName = "C1", LibName = "Foo5.dll", EntrypointName = "Wha5", + ReturnType = typeof(int), ParameterTypes = [typeof(string)], ReturnTypeReqMods = [typeof(int)], + ReturnTypeOptMods = [typeof(short)], ParameterTypeOptMods = [[typeof(double)]], ParameterTypeReqMods = [[typeof(float)]]}]; + } + } + + [Theory] + [MemberData(nameof(TestData))] + public static void TestDefinePInvokeMethod(DpmParams p) + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + MethodBuilder mb = tb.DefinePInvokeMethod(p.MethodName, p.LibName, p.EntrypointName, p.Attributes, p.ManagedCallConv, p.ReturnType, + p.ReturnTypeReqMods, p.ReturnTypeOptMods, p.ParameterTypes, p.ParameterTypeReqMods, p.ParameterTypeOptMods, p.NativeCallConv, p.Charset); + mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); + Type t = tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo m = typeFromDisk.GetMethod(p.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(m); + VerifyPInvokeMethod(t, m, p, mlc.CoreAssembly); + } + } + } + + internal static void VerifyPInvokeMethod(Type type, MethodInfo method, DpmParams p, Assembly coreAssembly) + { + Assert.Equal(type.FullName, method.DeclaringType.FullName); + Assert.Equal(p.MethodName, method.Name); + Assert.Equal(p.Attributes, method.Attributes); + Assert.Equal(p.ManagedCallConv, method.CallingConvention); + Assert.Equal(coreAssembly.GetType(p.ReturnType.FullName), method.ReturnType); + + ParameterInfo[] parameters = method.GetParameters(); + Assert.Equal(coreAssembly.GetType(p.ParameterTypes[0].FullName), parameters[0].ParameterType); + + CustomAttributeData dllAttrData = method.GetCustomAttributesData()[0]; + if (dllAttrData.AttributeType.FullName == typeof(PreserveSigAttribute).FullName) + { + dllAttrData = method.GetCustomAttributesData()[1]; + } + + Assert.Equal(coreAssembly.GetType(typeof(DllImportAttribute).FullName), dllAttrData.AttributeType); + Assert.Equal(p.LibName, dllAttrData.ConstructorArguments[0].Value); + foreach (CustomAttributeNamedArgument namedArg in dllAttrData.NamedArguments) + { + if (namedArg.MemberName == "EntryPoint") + { + Assert.Equal(p.EntrypointName, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "CharSet") + { + Assert.Equal(p.Charset, (CharSet)namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "SetLastError") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "ExactSpelling") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "BestFitMapping") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "ThrowOnUnmappableChar") + { + Assert.Equal(false, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "PreserveSig") + { + Assert.Equal(true, namedArg.TypedValue.Value); + } + else if (namedArg.MemberName == "CallingConvention") + { + Assert.Equal(p.NativeCallConv, (CallingConvention)namedArg.TypedValue.Value); + } + } + + IList returnTypeOptMods = method.ReturnParameter.GetOptionalCustomModifiers(); + if (p.ReturnTypeOptMods == null) + { + Assert.Equal(0, returnTypeOptMods.Count); + } + else + { + Assert.Equal(coreAssembly.GetType(p.ReturnTypeOptMods[0].FullName), returnTypeOptMods[0]); + } + + IList returnTypeReqMods = method.ReturnParameter.GetRequiredCustomModifiers(); + if (p.ReturnTypeReqMods == null) + { + Assert.Equal(0, returnTypeReqMods.Count); + } + else + { + Assert.Equal(coreAssembly.GetType(p.ReturnTypeReqMods[0].FullName), returnTypeReqMods[0]); + } + + if (p.ParameterTypeOptMods == null) + { + foreach (ParameterInfo pi in method.GetParameters()) + { + Assert.Equal(0, pi.GetOptionalCustomModifiers().Length); + } + } + else + { + Assert.Equal(parameters.Length, p.ParameterTypeOptMods.Length); + for (int i = 0; i < p.ParameterTypeOptMods.Length; i++) + { + Type[] mods = parameters[i].GetOptionalCustomModifiers(); + Assert.Equal(coreAssembly.GetType(p.ParameterTypeOptMods[i][0].FullName), mods[0]); + } + } + + if (p.ParameterTypeReqMods == null) + { + foreach (ParameterInfo pi in method.GetParameters()) + { + Assert.Equal(0, pi.GetRequiredCustomModifiers().Length); + } + } + else + { + Assert.Equal(parameters.Length, p.ParameterTypeReqMods.Length); + for (int i = 0; i < p.ParameterTypeReqMods.Length; i++) + { + Type[] mods = parameters[i].GetRequiredCustomModifiers(); + Assert.Equal(coreAssembly.GetType(p.ParameterTypeReqMods[i][0].FullName), mods[0]); + } + } + } + + [Fact] + public void DefineTypeInitializer() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + FieldBuilder greetingField = tb.DefineField("Greeting", typeof(string), FieldAttributes.Private | FieldAttributes.Static); + ConstructorBuilder constructor = tb.DefineTypeInitializer(); + ILGenerator constructorIlGenerator = constructor.GetILGenerator(); + constructorIlGenerator.Emit(OpCodes.Ldstr, "hello"); + constructorIlGenerator.Emit(OpCodes.Stsfld, greetingField); + constructorIlGenerator.Emit(OpCodes.Ret); + + tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + FieldInfo createdField = typeFromDisk.GetField("Greeting", BindingFlags.NonPublic | BindingFlags.Static); + Assert.Equal("hello", createdField.GetValue(null)); + tlc.Unload(); + } + } + + [Fact] + public static void DefineUninitializedDataTest() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + FieldBuilder myFieldBuilder = tb.DefineUninitializedData("MyGreeting", 4, FieldAttributes.Public); + var loadAddressMethod = tb.DefineMethod("LoadAddress", MethodAttributes.Public | MethodAttributes.Static, typeof(IntPtr), null); + var methodIL = loadAddressMethod.GetILGenerator(); + methodIL.Emit(OpCodes.Ldsflda, myFieldBuilder); + methodIL.Emit(OpCodes.Ret); + + Type t = tb.CreateType(); + saveMethod.Invoke(ab, [file.Path]); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.GetType("MyType"); + byte[] initBytes = [4, 3, 2, 1]; + nint myIntPtr = Marshal.AllocHGlobal(4); + nint intptrTemp = Marshal.AllocHGlobal(4); + for (int j = 0; j < 4; j++) + { + Marshal.WriteByte(myIntPtr + j, initBytes[j]); + } + object myObj = Marshal.PtrToStructure(myIntPtr, typeFromDisk.GetField("MyGreeting").FieldType); + Marshal.StructureToPtr(myObj, intptrTemp, false); + for (int j = 0; j < 4; j++) + { + Assert.Equal(initBytes[j], Marshal.ReadByte(intptrTemp, j)); + } + Marshal.FreeHGlobal(myIntPtr); + Marshal.FreeHGlobal(intptrTemp); + tlc.Unload(); + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 79b3f309d3d3ef..8e6d7227c02a7b 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -515,9 +515,9 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); Module m = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First(); - Type[] type1Params = m.GetTypes()[2].GetGenericArguments(); + Type[] type1Params = m.GetTypes()[0].GetGenericArguments(); Type[] type2Params = m.GetTypes()[1].GetGenericArguments(); - Type[] type3Params = m.GetTypes()[0].GetGenericArguments(); + Type[] type3Params = m.GetTypes()[2].GetGenericArguments(); Assert.Equal("U", type1Params[0].Name); Assert.Empty(type1Params[0].GetTypeInfo().GetGenericParameterConstraints()); @@ -529,10 +529,10 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() Assert.Equal("TOne", type3Params[0].Name); Assert.Equal(nameof(EmptyTestClass), type3Params[0].GetTypeInfo().GetGenericParameterConstraints()[0].Name); - Type[] method11Params = m.GetTypes()[2].GetMethod("TwoParameters").GetGenericArguments(); - Type[] method12Params = m.GetTypes()[2].GetMethod("FiveTypeParameters").GetGenericArguments(); + Type[] method11Params = m.GetTypes()[0].GetMethod("TwoParameters").GetGenericArguments(); + Type[] method12Params = m.GetTypes()[0].GetMethod("FiveTypeParameters").GetGenericArguments(); Assert.Equal(nameof(IMultipleMethod), method12Params[2].GetTypeInfo().GetGenericParameterConstraints()[0].Name); - Type[] method13Params = m.GetTypes()[2].GetMethod("OneParameter").GetGenericArguments(); + Type[] method13Params = m.GetTypes()[0].GetMethod("OneParameter").GetGenericArguments(); Type[] method21Params = m.GetTypes()[1].GetMethod("TestMethod").GetGenericArguments(); Assert.Equal("M", method11Params[0].Name); diff --git a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj index efb87722f12940..f70ec0e3d751ed 100644 --- a/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj +++ b/src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj @@ -68,7 +68,8 @@ - + + From ca1b161e3c987bc2c14f695bffce3bb391b8aaa0 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 19 Jan 2024 16:29:16 -0800 Subject: [PATCH 140/189] Remove some timeouts and reorganize test (#97115) Ensure target process runs to completion. Simplify steps in test and dispose of additional classes. --- .../TestsManifestGeneration.Etw.cs | 168 +++++++++--------- 1 file changed, 81 insertions(+), 87 deletions(-) diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs index d3001cc3690323..4973ad18f74aab 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; +using System.Threading; #if USE_MDT_EVENTSOURCE using Microsoft.Diagnostics.Tracing; #else @@ -14,8 +16,6 @@ using Xunit; using SdtEventSources; -using System.Diagnostics; -using System.Threading; using Microsoft.Diagnostics.Tracing.Session; using Microsoft.DotNet.RemoteExecutor; using Microsoft.Diagnostics.Tracing; @@ -35,42 +35,38 @@ public void Test_EventSource_EtwManifestGeneration() { var pid = Process.GetCurrentProcess().Id; var etlFileName = $"file.{pid}.etl"; - var tracesession = new TraceEventSession("testname", etlFileName); - - tracesession.EnableProvider(nameof(SimpleEventSource)); - RemoteInvokeOptions localOptions = new RemoteInvokeOptions { TimeOut = 300_000 /* ms */ }; - using (RemoteInvokeHandle handle = RemoteExecutor.Invoke(() => - { - var es = new SimpleEventSource(); - for (var i = 0; i < 100; i++) - { - es.WriteSimpleInt(i); - Thread.Sleep(100); - } - }, localOptions)) + // Start the trace session + using (var traceSession = new TraceEventSession(nameof(Test_EventSource_EtwManifestGeneration), etlFileName)) { - Thread.Sleep(TimeSpan.FromSeconds(5)); - - tracesession.Flush(); + // Enable the provider of interest. + traceSession.EnableProvider(nameof(SimpleEventSource)); - tracesession.DisableProvider(nameof(SimpleEventSource)); - tracesession.Dispose(); - - var manifestExists = false; - var max_retries = 50; - - for (int i = 0; i < max_retries; i++) + // Launch the target process to collect data + using (RemoteInvokeHandle handle = RemoteExecutor.Invoke(() => { - if (VerifyManifestAndRemoveFile(etlFileName)) + using var es = new SimpleEventSource(); + + // 50 * 100 = 5 seconds + for (var i = 0; i < 50; i++) { - manifestExists = true; - break; + es.WriteSimpleInt(i); + Thread.Sleep(100); } - Thread.Sleep(1000); + })) + { + handle.Process.WaitForExit(); } - Assert.True(manifestExists); + + // Flush session and disable the provider. + traceSession.Flush(); + traceSession.DisableProvider(nameof(SimpleEventSource)); } + + // Wait for the ETL file to flush to disk + Thread.Sleep(TimeSpan.FromSeconds(2)); + + Assert.True(VerifyManifestAndRemoveFile(etlFileName)); } [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServerAndRemoteExecutorSupported))] @@ -80,87 +76,85 @@ public void Test_EventSource_EtwManifestGenerationRollover() var pid = Process.GetCurrentProcess().Id; var initialFileName = $"initialFile.{pid}.etl"; var rolloverFileName = $"rolloverFile.{pid}.etl"; - var tracesession = new TraceEventSession("testname", initialFileName); - - tracesession.EnableProvider(nameof(SimpleEventSource)); - using (RemoteInvokeHandle handle = RemoteExecutor.Invoke(() => + // Start the trace session + using (var traceSession = new TraceEventSession(nameof(Test_EventSource_EtwManifestGenerationRollover), initialFileName)) { - var es = new SimpleEventSource(); - for (var i = 0; i < 100; i++) - { - es.WriteSimpleInt(i); - Thread.Sleep(100); - } - })) - { - Thread.Sleep(TimeSpan.FromSeconds(5)); + // Enable the provider of interest. + traceSession.EnableProvider(nameof(SimpleEventSource)); - tracesession.Flush(); + // Launch the target process to collect data + using (RemoteInvokeHandle handle = RemoteExecutor.Invoke(() => + { + using var es = new SimpleEventSource(); - tracesession.SetFileName(rolloverFileName); + // 100 * 100 = 10 seconds + for (var i = 0; i < 100; i++) + { + es.WriteSimpleInt(i); + Thread.Sleep(100); + } + })) + { + // Wait for some time to collect events + Thread.Sleep(TimeSpan.FromSeconds(5)); - Thread.Sleep(TimeSpan.FromSeconds(5)); + traceSession.Flush(); - tracesession.Flush(); + traceSession.SetFileName(rolloverFileName); - tracesession.DisableProvider(nameof(SimpleEventSource)); - tracesession.Dispose(); + // Wait for some time to collect events + Thread.Sleep(TimeSpan.FromSeconds(5)); - bool initialFileHasManifest = false; - bool rollOverFileHasManifest = false; + // Wait for the target process to exit. + handle.Process.WaitForExit(); - var max_retries = 50; - for (int i = 0; i < max_retries; i++) - { - if (VerifyManifestAndRemoveFile(initialFileName)) - { - initialFileHasManifest = true; - break; - } - Thread.Sleep(1000); + // Flush session and disable the provider. + traceSession.Flush(); + traceSession.DisableProvider(nameof(SimpleEventSource)); } - for (int i = 0; i < max_retries; i++) - { - if (VerifyManifestAndRemoveFile(rolloverFileName)) - { - rollOverFileHasManifest = true; - break; - } - Thread.Sleep(1000); - } - Assert.True(initialFileHasManifest); - Assert.True(rollOverFileHasManifest); } + + // Wait for the ETL files to flush to disk + Thread.Sleep(TimeSpan.FromSeconds(2)); + + Assert.True(VerifyManifestAndRemoveFile(initialFileName)); + Assert.True(VerifyManifestAndRemoveFile(rolloverFileName)); } private bool VerifyManifestAndRemoveFile(string fileName) { Assert.True(File.Exists(fileName)); - using var source = new ETWTraceEventSource(fileName); - Dictionary providers = new Dictionary(); int eventCount = 0; var sawManifestData = false; - source.Dynamic.All += (eventData) => + + using (var source = new ETWTraceEventSource(fileName)) { - eventCount++; - if (!providers.ContainsKey(eventData.ProviderName)) + source.Dynamic.All += (eventData) => { - providers[eventData.ProviderName] = 0; - } - providers[eventData.ProviderName]++; + eventCount++; + if (!providers.ContainsKey(eventData.ProviderName)) + { + providers[eventData.ProviderName] = 0; + } + providers[eventData.ProviderName]++; - if (eventData.ProviderName.Equals(nameof(SimpleEventSource)) && eventData.EventName.Equals("ManifestData")) - { - sawManifestData = true; - } - }; - source.Process(); - //File.Delete(fileName); + if (eventData.ProviderName.Equals(nameof(SimpleEventSource)) && eventData.EventName.Equals("ManifestData")) + { + sawManifestData = true; + } + }; + source.Process(); + } - if (!sawManifestData) + if (sawManifestData) + { + // Delete file if successfully processed. + File.Delete(fileName); + } + else { Console.WriteLine($"Did not see ManifestData event from {nameof(SimpleEventSource)}, test will fail. Additional info:"); Console.WriteLine($" file name {fileName}"); From ff93f2dc3619d28b87404d1f86439f4b1d6e8d02 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 20 Jan 2024 06:30:34 -0500 Subject: [PATCH 141/189] Add TaskCompletionSource.SetFromTask (#97077) --- .../src/System/Net/TaskExtensions.cs | 15 +-- .../src/Resources/Strings.resx | 5 +- .../Threading/Tasks/TaskCompletionSource.cs | 70 +++++++++++ .../Threading/Tasks/TaskCompletionSource_T.cs | 71 ++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 4 + .../Task/TaskCompletionSourceTResultTests.cs | 109 ++++++++++++++++++ .../Task/TaskCompletionSourceTests.cs | 107 +++++++++++++++++ 7 files changed, 366 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Net.Requests/src/System/Net/TaskExtensions.cs b/src/libraries/System.Net.Requests/src/System/Net/TaskExtensions.cs index 7732cf149317b8..bb0d98c40e6e1b 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/TaskExtensions.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/TaskExtensions.cs @@ -18,20 +18,7 @@ public static TaskCompletionSource ToApm( task.ContinueWith(completedTask => { - bool shouldInvokeCallback = false; - - if (completedTask.IsFaulted) - { - shouldInvokeCallback = tcs.TrySetException(completedTask.Exception!.InnerExceptions); - } - else if (completedTask.IsCanceled) - { - shouldInvokeCallback = tcs.TrySetCanceled(); - } - else - { - shouldInvokeCallback = tcs.TrySetResult(completedTask.Result); - } + bool shouldInvokeCallback = tcs.TrySetFromTask(completedTask); // Only invoke the callback if it exists AND we were able to transition the TCS // to the terminal state. If we couldn't transition the task it is because it was diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index ac57b0760021b2..d2bc611dbbbee9 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3527,6 +3527,9 @@ The tasks array included at least one null element. + + The provided task must have already completed. + Task<TResult>.ConfigureAwait does not support ConfigureAwaitOptions.SuppressThrowing. To suppress throwing, instead cast the Task<TResult> to its base class Task and await that with SuppressThrowing. @@ -4286,4 +4289,4 @@ This operation is not available because the reflection support was disabled at compile time. - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs index ee096f6816559a..f7ec3ea6efa65a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource.cs @@ -285,5 +285,75 @@ public bool TrySetCanceled(CancellationToken cancellationToken) return rval; } + + /// + /// Transition the underlying into the same completion state as the specified . + /// + /// The completed task whose completion status (including exception or cancellation information) should be copied to the underlying task. + /// is . + /// is not completed. + /// + /// The underlying is already in one of the three final states: + /// , , or . + /// + /// + /// This operation will return false if the is already in one of the three final states: + /// , , or . + /// + public void SetFromTask(Task completedTask) + { + if (!TrySetFromTask(completedTask)) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); + } + } + + /// + /// Attempts to transition the underlying into the same completion state as the specified . + /// + /// The completed task whose completion status (including exception or cancellation information) should be copied to the underlying task. + /// if the operation was successful; otherwise, . + /// is . + /// is not completed. + /// + /// This operation will return false if the is already in one of the three final states: + /// , , or . + /// + public bool TrySetFromTask(Task completedTask) + { + ArgumentNullException.ThrowIfNull(completedTask); + if (!completedTask.IsCompleted) + { + throw new ArgumentException(SR.Task_MustBeCompleted, nameof(completedTask)); + } + + // Try to transition to the appropriate final state based on the state of completedTask. + bool result = false; + switch (completedTask.Status) + { + case TaskStatus.RanToCompletion: + result = _task.TrySetResult(); + break; + + case TaskStatus.Canceled: + result = _task.TrySetCanceled(completedTask.CancellationToken, completedTask.GetCancellationExceptionDispatchInfo()); + break; + + case TaskStatus.Faulted: + result = _task.TrySetException(completedTask.GetExceptionDispatchInfos()); + break; + } + + // If we successfully transitioned to a final state, we're done. If we didn't, it's possible a concurrent operation + // is still in the process of completing the task, and callers of this method expect the task to already be fully + // completed when this method returns. As such, we spin until the task is completed, and then return whether this + // call successfully did the transition. + if (!result && !_task.IsCompleted) + { + _task.SpinUntilCompleted(); + } + + return result; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource_T.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource_T.cs index 977db4b94b25d6..2a7f76029d1197 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource_T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskCompletionSource_T.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.ExceptionServices; namespace System.Threading.Tasks { @@ -286,5 +287,75 @@ public bool TrySetCanceled(CancellationToken cancellationToken) return rval; } + + /// + /// Transition the underlying into the same completion state as the specified . + /// + /// The completed task whose completion status (including result, exception, or cancellation information) should be copied to the underlying task. + /// is . + /// is not completed. + /// + /// The underlying is already in one of the three final states: + /// , , or . + /// + /// + /// This operation will return false if the is already in one of the three final states: + /// , , or . + /// + public void SetFromTask(Task completedTask) + { + if (!TrySetFromTask(completedTask)) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); + } + } + + /// + /// Attempts to transition the underlying into the same completion state as the specified . + /// + /// The completed task whose completion status (including result, exception, or cancellation information) should be copied to the underlying task. + /// if the operation was successful; otherwise, . + /// is . + /// is not completed. + /// + /// This operation will return false if the is already in one of the three final states: + /// , , or . + /// + public bool TrySetFromTask(Task completedTask) + { + ArgumentNullException.ThrowIfNull(completedTask); + if (!completedTask.IsCompleted) + { + throw new ArgumentException(SR.Task_MustBeCompleted, nameof(completedTask)); + } + + // Try to transition to the appropriate final state based on the state of completedTask. + bool result = false; + switch (completedTask.Status) + { + case TaskStatus.RanToCompletion: + result = _task.TrySetResult(completedTask.Result); + break; + + case TaskStatus.Canceled: + result = _task.TrySetCanceled(completedTask.CancellationToken, completedTask.GetCancellationExceptionDispatchInfo()); + break; + + case TaskStatus.Faulted: + result = _task.TrySetException(completedTask.GetExceptionDispatchInfos()); + break; + } + + // If we successfully transitioned to a final state, we're done. If we didn't, it's possible a concurrent operation + // is still in the process of completing the task, and callers of this method expect the task to already be fully + // completed when this method returns. As such, we spin until the task is completed, and then return whether this + // call successfully did the transition. + if (!result && !_task.IsCompleted) + { + _task.SpinUntilCompleted(); + } + + return result; + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index d6442299edfcd5..0349694ed70241 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -15352,6 +15352,7 @@ public TaskCompletionSource(System.Threading.Tasks.TaskCreationOptions creationO public System.Threading.Tasks.Task Task { get { throw null; } } public void SetCanceled() { } public void SetCanceled(System.Threading.CancellationToken cancellationToken) { } + public void SetFromTask(System.Threading.Tasks.Task completedTask) { throw null; } public void SetException(System.Collections.Generic.IEnumerable exceptions) { } public void SetException(System.Exception exception) { } public void SetResult() { } @@ -15359,6 +15360,7 @@ public void SetResult() { } public bool TrySetCanceled(System.Threading.CancellationToken cancellationToken) { throw null; } public bool TrySetException(System.Collections.Generic.IEnumerable exceptions) { throw null; } public bool TrySetException(System.Exception exception) { throw null; } + public bool TrySetFromTask(System.Threading.Tasks.Task completedTask) { throw null; } public bool TrySetResult() { throw null; } } public partial class TaskCompletionSource @@ -15370,11 +15372,13 @@ public TaskCompletionSource(System.Threading.Tasks.TaskCreationOptions creationO public System.Threading.Tasks.Task Task { get { throw null; } } public void SetCanceled() { } public void SetCanceled(System.Threading.CancellationToken cancellationToken) { } + public void SetFromTask(System.Threading.Tasks.Task completedTask) { throw null; } public void SetException(System.Collections.Generic.IEnumerable exceptions) { } public void SetException(System.Exception exception) { } public void SetResult(TResult result) { } public bool TrySetCanceled() { throw null; } public bool TrySetCanceled(System.Threading.CancellationToken cancellationToken) { throw null; } + public bool TrySetFromTask(System.Threading.Tasks.Task completedTask) { throw null; } public bool TrySetException(System.Collections.Generic.IEnumerable exceptions) { throw null; } public bool TrySetException(System.Exception exception) { throw null; } public bool TrySetResult(TResult result) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTResultTests.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTResultTests.cs index 9a08e529abf93f..9bc44b683530e2 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTResultTests.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTResultTests.cs @@ -202,5 +202,114 @@ private static void AssertCompletedTcsFailsToCompleteAgain(TaskCompletionSour Assert.False(tcs.TrySetCanceled()); Assert.False(tcs.TrySetCanceled(default)); } + + [Fact] + public void SetFromTask_InvalidArgument_Throws() + { + TaskCompletionSource tcs = new(); + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(null)); + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(new TaskCompletionSource().Task)); + Assert.False(tcs.Task.IsCompleted); + + tcs.SetResult(null); + Assert.True(tcs.Task.IsCompletedSuccessfully); + + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(null)); + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(new TaskCompletionSource().Task)); + Assert.True(tcs.Task.IsCompletedSuccessfully); + } + + [Fact] + public void SetFromTask_AlreadyCompleted_ReturnsFalseOrThrows() + { + object result = new(); + TaskCompletionSource tcs = new(); + tcs.SetResult(result); + + Assert.False(tcs.TrySetFromTask(Task.FromResult(new object()))); + Assert.False(tcs.TrySetFromTask(Task.FromException(new Exception()))); + Assert.False(tcs.TrySetFromTask(Task.FromCanceled(new CancellationToken(canceled: true)))); + + Assert.Throws(() => tcs.SetFromTask(Task.FromResult(new object()))); + Assert.Throws(() => tcs.SetFromTask(Task.FromException(new Exception()))); + Assert.Throws(() => tcs.SetFromTask(Task.FromCanceled(new CancellationToken(canceled: true)))); + + Assert.True(tcs.Task.IsCompletedSuccessfully); + Assert.Same(result, tcs.Task.Result); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SetFromTask_CompletedSuccessfully(bool tryMethod) + { + TaskCompletionSource tcs = new(); + Task source = Task.FromResult(new object()); + + if (tryMethod) + { + Assert.True(tcs.TrySetFromTask(source)); + } + else + { + tcs.SetFromTask(source); + } + + Assert.Same(source.Result, tcs.Task.Result); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SetFromTask_Faulted(bool tryMethod) + { + TaskCompletionSource tcs = new(); + + var source = new TaskCompletionSource(); + source.SetException([new FormatException(), new DivideByZeroException()]); + + if (tryMethod) + { + Assert.True(tcs.TrySetFromTask(source.Task)); + } + else + { + tcs.SetFromTask(source.Task); + } + + Assert.True(tcs.Task.IsFaulted); + Assert.True(tcs.Task.Exception.InnerExceptions.Count == 2); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SetFromTask_Canceled(bool tryMethod) + { + TaskCompletionSource tcs = new(); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + Task source = Task.FromCanceled(cts.Token); + + if (tryMethod) + { + Assert.True(tcs.TrySetFromTask(source)); + } + else + { + tcs.SetFromTask(source); + } + + Assert.True(tcs.Task.IsCanceled); + try + { + tcs.Task.GetAwaiter().GetResult(); + } + catch (OperationCanceledException oce) + { + Assert.Equal(cts.Token, oce.CancellationToken); + } + } } } diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTests.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTests.cs index d97a4737639643..2cb19b41486f26 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTests.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskCompletionSourceTests.cs @@ -200,5 +200,112 @@ private static void AssertCompletedTcsFailsToCompleteAgain(TaskCompletionSource Assert.False(tcs.TrySetCanceled()); Assert.False(tcs.TrySetCanceled(default)); } + + [Fact] + public void SetFromTask_InvalidArgument_Throws() + { + TaskCompletionSource tcs = new(); + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(null)); + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(new TaskCompletionSource().Task)); + Assert.False(tcs.Task.IsCompleted); + + tcs.SetResult(); + Assert.True(tcs.Task.IsCompletedSuccessfully); + + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(null)); + AssertExtensions.Throws("completedTask", () => tcs.SetFromTask(new TaskCompletionSource().Task)); + Assert.True(tcs.Task.IsCompletedSuccessfully); + } + + [Fact] + public void SetFromTask_AlreadyCompleted_ReturnsFalseOrThrows() + { + TaskCompletionSource tcs = new(); + tcs.SetResult(); + + Assert.False(tcs.TrySetFromTask(Task.CompletedTask)); + Assert.False(tcs.TrySetFromTask(Task.FromException(new Exception()))); + Assert.False(tcs.TrySetFromTask(Task.FromCanceled(new CancellationToken(canceled: true)))); + + Assert.Throws(() => tcs.SetFromTask(Task.CompletedTask)); + Assert.Throws(() => tcs.SetFromTask(Task.FromException(new Exception()))); + Assert.Throws(() => tcs.SetFromTask(Task.FromCanceled(new CancellationToken(canceled: true)))); + + Assert.True(tcs.Task.IsCompletedSuccessfully); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SetFromTask_CompletedSuccessfully(bool tryMethod) + { + TaskCompletionSource tcs = new(); + Task source = Task.CompletedTask; + + if (tryMethod) + { + Assert.True(tcs.TrySetFromTask(source)); + } + else + { + tcs.SetFromTask(source); + } + + Assert.True(tcs.Task.IsCompletedSuccessfully); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SetFromTask_Faulted(bool tryMethod) + { + TaskCompletionSource tcs = new(); + + var source = new TaskCompletionSource(); + source.SetException([new FormatException(), new DivideByZeroException()]); + + if (tryMethod) + { + Assert.True(tcs.TrySetFromTask(source.Task)); + } + else + { + tcs.SetFromTask(source.Task); + } + + Assert.True(tcs.Task.IsFaulted); + Assert.True(tcs.Task.Exception.InnerExceptions.Count == 2); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void SetFromTask_Canceled(bool tryMethod) + { + TaskCompletionSource tcs = new(); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + Task source = Task.FromCanceled(cts.Token); + + if (tryMethod) + { + Assert.True(tcs.TrySetFromTask(source)); + } + else + { + tcs.SetFromTask(source); + } + + Assert.True(tcs.Task.IsCanceled); + try + { + tcs.Task.GetAwaiter().GetResult(); + } + catch (OperationCanceledException oce) + { + Assert.Equal(cts.Token, oce.CancellationToken); + } + } } } From fcbcfcc9759a414a3fd5387ee3b642c4871dd056 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Sat, 20 Jan 2024 14:40:38 +0200 Subject: [PATCH 142/189] [mono][interp] Small code refactoring for SSA (#97249) * [mono][interp] Move optimization related code out of transform.c Use interp_ prefix for non-static methods within interpreter (mono_interp seems rather long) Use interp_ prefix, instead of .._interp_..., for consistency. Use ins everywhere in method names for consistency, instead of inst. * [mono][interp] Pass ref to var storage in interp inst In order to facilitate overwritting of sregs/dreg during instruction iterations. Also enable iterating only on sregs. * [mono][interp] Print invalid il offset in aligned fashion * [mono][interp] Remove irrelevant stats * [mono][interp] Renaming of local to var Local can have multiple meanings. Use it to refer to IL locals from now. All IL locals are vars. Vars can be local (single bblock use) or global. * [mono][interp] Remove flags and use bit fields instead Makes the code clearer and it is easier to maintain. --- src/mono/mono/mini/CMakeLists.txt | 1 + src/mono/mono/mini/interp/interp-internals.h | 8 - src/mono/mono/mini/interp/interp.c | 8 - src/mono/mono/mini/interp/jiterpreter.c | 2 +- src/mono/mono/mini/interp/transform-opt.c | 2580 +++++++++++++ src/mono/mono/mini/interp/transform-simd.c | 10 +- src/mono/mono/mini/interp/transform.c | 3417 +++--------------- src/mono/mono/mini/interp/transform.h | 128 +- 8 files changed, 3105 insertions(+), 3049 deletions(-) create mode 100644 src/mono/mono/mini/interp/transform-opt.c diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 3628dd3a19de26..5e6ebe9ce49dc4 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -314,6 +314,7 @@ set(interp_sources interp/mintops.h interp/mintops.c interp/transform.c + interp/transform-opt.c interp/tiering.h interp/tiering.c interp/jiterpreter.c diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 9be34b46d84570..72ef097eac22d7 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -277,15 +277,7 @@ typedef struct { gint64 methods_transformed; gint64 cprop_time; gint64 super_instructions_time; - gint32 stloc_nps; - gint32 movlocs; - gint32 copy_propagations; - gint32 constant_folds; - gint32 ldlocas_removed; - gint32 killed_instructions; gint32 emitted_instructions; - gint32 super_instructions; - gint32 added_pop_count; gint32 inlined_methods; gint32 inline_failures; } MonoInterpStats; diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index ad1c504ee1bd4e..23bf79620a73e2 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -8690,14 +8690,6 @@ register_interp_stats (void) mono_counters_register ("Methods transformed", MONO_COUNTER_INTERP | MONO_COUNTER_LONG, &mono_interp_stats.methods_transformed); mono_counters_register ("Total cprop time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.cprop_time); mono_counters_register ("Total super instructions time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.super_instructions_time); - mono_counters_register ("STLOC_NP count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.stloc_nps); - mono_counters_register ("MOVLOC count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.movlocs); - mono_counters_register ("Copy propagations", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.copy_propagations); - mono_counters_register ("Added pop count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.added_pop_count); - mono_counters_register ("Constant folds", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.constant_folds); - mono_counters_register ("Ldlocas removed", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.ldlocas_removed); - mono_counters_register ("Super instructions", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.super_instructions); - mono_counters_register ("Killed instructions", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.killed_instructions); mono_counters_register ("Emitted instructions", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.emitted_instructions); mono_counters_register ("Methods inlined", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.inlined_methods); mono_counters_register ("Inline failures", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.inline_failures); diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index ceb6a66679647e..0d4e17bf346f53 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -773,7 +773,7 @@ build_address_taken_bitset (TransformData *td, InterpBasicBlock *bb, guint32 bit for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { if (ins->opcode == MINT_LDLOCA_S) { InterpMethod *imethod = td->rtm; - InterpLocal *loc = &td->locals[ins->sregs[0]]; + InterpVar *loc = &td->vars [ins->sregs[0]]; // Allocate on demand so if a method contains no ldlocas we don't allocate the bitset if (!imethod->address_taken_bits) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c new file mode 100644 index 00000000000000..c6fc3a069c045d --- /dev/null +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -0,0 +1,2580 @@ +/* + * Optimizations for interpreter codegen + */ + +#include "mintops.h" +#include "transform.h" + +// Allocates var at the offset that tos points to, also updating it. +static int +alloc_var_offset (TransformData *td, int local, gint32 *ptos) +{ + int size, offset; + + offset = *ptos; + size = td->vars [local].size; + + if (td->vars [local].simd) + offset = ALIGN_TO (offset, MINT_SIMD_ALIGNMENT); + + td->vars [local].offset = offset; + + *ptos = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); + + return td->vars [local].offset; +} + +int +interp_alloc_global_var_offset (TransformData *td, int var) +{ + return alloc_var_offset (td, var, &td->total_locals_size); +} + +static void +set_var_live_range (TransformData *td, int var, int ins_index) +{ + // We don't track liveness yet for global vars + if (td->vars [var].global) + return; + if (td->vars [var].live_start == -1) + td->vars [var].live_start = ins_index; + td->vars [var].live_end = ins_index; +} + +static void +set_var_live_range_cb (TransformData *td, int *pvar, gpointer data) +{ + set_var_live_range (td, *pvar, (int)(gsize)data); +} + +static void +initialize_global_var (TransformData *td, int var, int bb_index) +{ + // Check if already handled + if (td->vars [var].global) + return; + + if (td->vars [var].bb_index == -1) { + td->vars [var].bb_index = bb_index; + } else if (td->vars [var].bb_index != bb_index) { + // var used in multiple basic blocks + if (td->verbose_level) + g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); + interp_alloc_global_var_offset (td, var); + td->vars [var].global = TRUE; + } +} + +static void +initialize_global_var_cb (TransformData *td, int *pvar, gpointer data) +{ + initialize_global_var (td, *pvar, (int)(gsize)data); +} + +static void +initialize_global_vars (TransformData *td) +{ + InterpBasicBlock *bb; + + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + if (opcode == MINT_NOP) { + continue; + } else if (opcode == MINT_LDLOCA_S) { + int var = ins->sregs [0]; + // If global flag is set, it means its offset was already allocated + if (!td->vars [var].global) { + if (td->verbose_level) + g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); + interp_alloc_global_var_offset (td, var); + td->vars [var].global = TRUE; + } + } + interp_foreach_ins_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); + } + } + td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT); +} + +// Data structure used for offset allocation of call args +typedef struct { + InterpInst **active_calls; + int active_calls_count; + int active_calls_capacity; + // A deferred call stack implemented as a linked list + GSList *deferred_calls; +} ActiveCalls; + +static void +init_active_calls (TransformData *td, ActiveCalls *ac) +{ + ac->active_calls_count = 0; + ac->active_calls_capacity = 5; + ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); + ac->deferred_calls = NULL; +} + +static void +reinit_active_calls (TransformData *td, ActiveCalls *ac) +{ + ac->active_calls_count = 0; + ac->deferred_calls = NULL; +} + +static void +add_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) +{ + // Check if already added + if (call->flags & INTERP_INST_FLAG_ACTIVE_CALL) + return; + + if (ac->active_calls_count == ac->active_calls_capacity) { + InterpInst **old = ac->active_calls; + ac->active_calls_capacity *= 2; + ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); + memcpy (ac->active_calls, old, ac->active_calls_count * sizeof (InterpInst*)); + } + ac->active_calls [ac->active_calls_count] = call; + ac->active_calls_count++; + + // Mark a flag on it so we don't have to lookup the array with every argument store. + call->flags |= INTERP_INST_FLAG_ACTIVE_CALL; +} + +/** + * Function allocates offsets of resolved calls following a constraint + * where the base offset of a call must be greater than the offset of any argument of other active call args. + * + * Function first removes the call from an array of active calls. If a match is found, + * the call is removed from the array by moving the last entry into its place. Otherwise, it is a call without arguments. + * + * If there are active calls, the call in question is push onto the stack as a deferred call. + * The call contains a list of other active calls on which it depends. Those calls need to be resolved first in order to determine optimal base offset for the call in question. + * Otherwise, if there are no active calls, function starts resolving the call in question and deferred calls from the stack. + * + * For each call, function computes the base offset, the offset of each call argument starting from a base offset, and stores the computed call offset into a InterpInst. + * The base offset is computed as max offset of all call offsets on which the call depends. + * Stack ensures that all call offsets on which the call depends are calculated before the call in question, by deferring calls from the last to the first one. + */ +static void +end_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) +{ + // Remove call from array + for (int i = 0; i < ac->active_calls_count; i++) { + if (ac->active_calls [i] == call) { + ac->active_calls_count--; + // Since this entry is removed, move the last entry into it + if (ac->active_calls_count > 0 && i < ac->active_calls_count) + ac->active_calls [i] = ac->active_calls [ac->active_calls_count]; + break; + } + } + + // Push active call that should be resolved onto the stack + call->info.call_info->call_deps = NULL; + if (ac->active_calls_count) { + for (int i = 0; i < ac->active_calls_count; i++) + call->info.call_info->call_deps = g_slist_prepend_mempool (td->mempool, call->info.call_info->call_deps, ac->active_calls [i]); + ac->deferred_calls = g_slist_prepend_mempool (td->mempool, ac->deferred_calls, call); + } else { + // If no other active calls, current active call and all deferred calls can be resolved from the stack + InterpInst *deferred_call = call; + while (deferred_call) { + // `base_offset` is a relative offset (to the start of the call args stack) where the args for this call reside. + // The deps for a call represent the list of active calls at the moment when the call ends. This means that all deps for a call end after the call in question. + // Given we iterate over the list of deferred calls from the last to the first one to end, all deps of a call are guaranteed to have been processed at this point. + int base_offset = 0; + for (GSList *list = deferred_call->info.call_info->call_deps; list; list = list->next) { + int end_offset = ((InterpInst*)list->data)->info.call_info->call_end_offset; + if (end_offset > base_offset) + base_offset = end_offset; + } + deferred_call->info.call_info->call_offset = base_offset; + // Compute to offset of each call argument + int *call_args = deferred_call->info.call_info->call_args; + if (call_args && (*call_args != -1)) { + int var = *call_args; + while (var != -1) { + alloc_var_offset (td, var, &base_offset); + call_args++; + var = *call_args; + } + } + deferred_call->info.call_info->call_end_offset = ALIGN_TO (base_offset, MINT_STACK_ALIGNMENT); + + if (ac->deferred_calls) { + deferred_call = (InterpInst*) ac->deferred_calls->data; + ac->deferred_calls = ac->deferred_calls->next; + } else + deferred_call = NULL; + } + } +} + +// Data structure used for offset allocation of local vars + +typedef struct { + int var; + gboolean is_alive; +} ActiveVar; + +typedef struct { + ActiveVar *active_vars; + int active_vars_count; + int active_vars_capacity; +} ActiveVars; + +static void +init_active_vars (TransformData *td, ActiveVars *av) +{ + av->active_vars_count = 0; + av->active_vars_capacity = MAX (td->vars_size / td->bb_count, 10); + av->active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVars)); +} + +static void +reinit_active_vars (TransformData *td, ActiveVars *av) +{ + av->active_vars_count = 0; +} + +static void +add_active_var (TransformData *td, ActiveVars *av, int var) +{ + if (av->active_vars_count == av->active_vars_capacity) { + av->active_vars_capacity *= 2; + ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVar)); + memcpy (new_array, av->active_vars, av->active_vars_count * sizeof (ActiveVar)); + av->active_vars = new_array; + } + av->active_vars [av->active_vars_count].var = var; + av->active_vars [av->active_vars_count].is_alive = TRUE; + av->active_vars_count++; +} + +static void +end_active_var (TransformData *td, ActiveVars *av, int var) +{ + // Iterate over active vars, set the entry associated with var as !is_alive + for (int i = 0; i < av->active_vars_count; i++) { + if (av->active_vars [i].var == var) { + av->active_vars [i].is_alive = FALSE; + return; + } + } +} + +static void +compact_active_vars (TransformData *td, ActiveVars *av, gint32 *current_offset) +{ + if (!av->active_vars_count) + return; + int i = av->active_vars_count - 1; + while (i >= 0 && !av->active_vars [i].is_alive) { + av->active_vars_count--; + *current_offset = td->vars [av->active_vars [i].var].offset; + i--; + } +} + +static void +dump_active_vars (TransformData *td, ActiveVars *av) +{ + if (td->verbose_level) { + g_print ("active :"); + for (int i = 0; i < av->active_vars_count; i++) { + if (av->active_vars [i].is_alive) + g_print (" %d (end %d),", av->active_vars [i].var, td->vars [av->active_vars [i].var].live_end); + } + g_print ("\n"); + } +} + +void +interp_alloc_offsets (TransformData *td) +{ + InterpBasicBlock *bb; + ActiveCalls ac; + ActiveVars av; + + if (td->verbose_level) + g_print ("\nvar offset allocator iteration\n"); + + initialize_global_vars (td); + + init_active_vars (td, &av); + init_active_calls (td, &ac); + + int final_total_locals_size = td->total_locals_size; + // We now have the top of stack offset. All local regs are allocated after this offset, with each basic block + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + int ins_index = 0; + if (td->verbose_level) + g_print ("BB%d\n", bb->index); + + reinit_active_calls (td, &ac); + reinit_active_vars (td, &av); + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (ins->opcode == MINT_NOP) + continue; + if (ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_VT || + ins->opcode == MINT_NEWOBJ_SLOW || ins->opcode == MINT_NEWOBJ_STRING) { + // The offset allocator assumes that the liveness of destination var starts + // after the source vars, which means the destination var can be allocated + // at the same offset as some of the arguments. However, for newobj opcodes, + // the created object is set before the call is made. We solve this by making + // sure that the dreg is not allocated in the param area, so there is no + // risk of conflicts. + td->vars [ins->dreg].no_call_args = TRUE; + } + if (ins->flags & INTERP_INST_FLAG_CALL) { + if (ins->info.call_info && ins->info.call_info->call_args) { + int *call_args = ins->info.call_info->call_args; + guint16 pair_sregs [MINT_MOV_PAIRS_MAX]; + guint16 pair_dregs [MINT_MOV_PAIRS_MAX]; + int num_pairs = 0; + int var = *call_args; + + while (var != -1) { + if (td->vars [var].global || + !td->local_ref_count || td->local_ref_count [var] > 1 || + td->vars [var].no_call_args) { + // Some vars can't be allocated on the call args stack, since the constraint is that + // call args vars die after the call. This isn't necessarily true for global vars or + // vars that are used by other instructions aside from the call. + // We need to copy the var into a new tmp var + int new_var = interp_create_var (td, td->vars [var].type); + td->vars [new_var].call = ins; + td->vars [new_var].call_args = TRUE; + + int mt = mono_mint_type (td->vars [var].type); + if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX && var <= G_MAXUINT16 && new_var <= G_MAXUINT16) { + // We store these in the instruction data slots so we do this optimizations only if they fit + pair_sregs [num_pairs] = (guint16)var; + pair_dregs [num_pairs] = (guint16)new_var; + num_pairs++; + // The arg of the call is no longer global + *call_args = new_var; + } else { + int opcode = interp_get_mov_for_type (mt, FALSE); + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + interp_ins_set_dreg (new_inst, new_var); + interp_ins_set_sreg (new_inst, var); + if (opcode == MINT_MOV_VT) + new_inst->data [0] = GINT_TO_UINT16 (td->vars [var].size); + // The arg of the call is no longer global + *call_args = new_var; + // Also update liveness for this instruction + interp_foreach_ins_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb); + ins_index++; + } + } else { + // Flag this var as it has special storage on the call args stack + td->vars [var].call = ins; + td->vars [var].call_args = TRUE; + } + call_args++; + var = *call_args; + } + if (num_pairs > 0) { + int i; + for (i = 0; i < num_pairs; i++) { + set_var_live_range (td, pair_sregs [i], ins_index); + set_var_live_range (td, pair_dregs [i], ins_index); + } + if (num_pairs == 1) { + int mt = mono_mint_type (td->vars [pair_sregs [0]].type); + int opcode = interp_get_mov_for_type (mt, FALSE); + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + interp_ins_set_dreg (new_inst, pair_dregs [0]); + interp_ins_set_sreg (new_inst, pair_sregs [0]); + } else { + // Squash together multiple moves to the param area into a single opcode + int opcode = MINT_MOV_8_2 + num_pairs - 2; + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + int k = 0; + for (i = 0; i < num_pairs; i++) { + new_inst->data [k++] = pair_dregs [i]; + new_inst->data [k++] = pair_sregs [i]; + } + } + ins_index++; + } + } + } + // Set live_start and live_end for every referenced local that is not global + interp_foreach_ins_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb); + ins_index++; + } + gint32 current_offset = td->total_locals_size; + + ins_index = 0; + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + gboolean is_call = ins->flags & INTERP_INST_FLAG_CALL; + + if (opcode == MINT_NOP) + continue; + + if (td->verbose_level) { + g_print ("\tins_index %d\t", ins_index); + interp_dump_ins (ins, td->data_items); + } + + // Expire source vars. We first mark them as not alive and then compact the array + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + int var = ins->sregs [i]; + if (var == MINT_CALL_ARGS_SREG) + continue; + if (!td->vars [var].global && td->vars [var].live_end == ins_index) { + g_assert (!td->vars [var].call_args); + end_active_var (td, &av, var); + } + } + if (opcode >= MINT_MOV_8_2 && opcode <= MINT_MOV_8_4) { + // These opcodes have multiple dvars, which overcomplicate things, so they are + // marked as having no svars/dvars, for now. Special case it. + int num_pairs = 2 + opcode - MINT_MOV_8_2; + for (int i = 0; i < num_pairs; i++) { + int var = ins->data [2 * i + 1]; + if (!td->vars [var].global && td->vars [var].live_end == ins_index) + end_active_var (td, &av, var); + } + } + + if (is_call) + end_active_call (td, &ac, ins); + + compact_active_vars (td, &av, ¤t_offset); + + // Alloc dreg local starting at the stack_offset + if (mono_interp_op_dregs [opcode]) { + int var = ins->dreg; + + if (td->vars [var].call_args) { + add_active_call (td, &ac, td->vars [var].call); + } else if (!td->vars [var].global && td->vars [var].offset == -1) { + alloc_var_offset (td, var, ¤t_offset); + if (current_offset > final_total_locals_size) + final_total_locals_size = current_offset; + + if (td->verbose_level) + g_print ("alloc var %d to offset %d\n", var, td->vars [var].offset); + + if (td->vars [var].live_end > ins_index) { + // if dreg is still used in the basic block, add it to the active list + add_active_var (td, &av, var); + } else { + current_offset = td->vars [var].offset; + } + } + } + if (td->verbose_level) + dump_active_vars (td, &av); + ins_index++; + } + } + final_total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); + + // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) + // then also update td->total_locals_size to account for this space. + td->param_area_offset = final_total_locals_size; + for (unsigned int i = 0; i < td->vars_size; i++) { + // These are allocated separately at the end of the stack + if (td->vars [i].call_args) { + td->vars [i].offset += td->param_area_offset; + final_total_locals_size = MAX (td->vars [i].offset + td->vars [i].size, final_total_locals_size); + } + } + td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); +} + +static GString* +interp_get_bb_links (InterpBasicBlock *bb) +{ + GString *str = g_string_new (""); + + if (bb->in_count) { + g_string_append_printf (str, "IN (%d", bb->in_bb [0]->index); + for (int i = 1; i < bb->in_count; i++) + g_string_append_printf (str, " %d", bb->in_bb [i]->index); + g_string_append_printf (str, "), "); + } else { + g_string_append_printf (str, "IN (nil), "); + } + + if (bb->out_count) { + g_string_append_printf (str, "OUT (%d", bb->out_bb [0]->index); + for (int i = 1; i < bb->out_count; i++) + g_string_append_printf (str, " %d", bb->out_bb [i]->index); + g_string_append_printf (str, ")"); + } else { + g_string_append_printf (str, "OUT (nil)"); + } + + return str; +} + +static void +mark_bb_as_dead (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *replace_bb) +{ + // Update IL offset to bb mapping so that offset_to_bb doesn't point to dead + // bblocks. This mapping can still be needed when computing clause ranges. Since + // multiple IL offsets can end up pointing to same bblock after optimizations, + // make sure we update mapping for all of them + // + // To avoid scanning the entire offset_to_bb array, we scan only in the vicinity + // of the IL offset of bb. We can stop search when we encounter a different bblock. + g_assert (bb->il_offset >= 0); + for (int il_offset = bb->il_offset; il_offset >= 0; il_offset--) { + if (td->offset_to_bb [il_offset] == bb) + td->offset_to_bb [il_offset] = replace_bb; + else if (td->offset_to_bb [il_offset]) + break; + } + for (guint32 il_offset = bb->il_offset + 1; il_offset < td->header->code_size; il_offset++) { + if (td->offset_to_bb [il_offset] == bb) + td->offset_to_bb [il_offset] = replace_bb; + else if (td->offset_to_bb [il_offset]) + break; + } + + bb->dead = TRUE; + // bb should never be used/referenced after this +} + +/* Merges two consecutive bbs (in code order) into a single one */ +static void +interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *bbadd) +{ + g_assert (bbadd->in_count == 1 && bbadd->in_bb [0] == bb); + g_assert (bb->next_bb == bbadd); + + // Remove the branch instruction to the invalid bblock + if (bb->last_ins) { + InterpInst *last_ins = (bb->last_ins->opcode != MINT_NOP) ? bb->last_ins : interp_prev_ins (bb->last_ins); + if (last_ins) { + if (last_ins->opcode == MINT_BR) { + g_assert (last_ins->info.target_bb == bbadd); + interp_clear_ins (last_ins); + } else if (last_ins->opcode == MINT_SWITCH) { + // Weird corner case where empty switch can branch by default to next instruction + last_ins->opcode = MINT_NOP; + } + } + } + + // Append all instructions from bbadd to bb + if (bb->last_ins) { + if (bbadd->first_ins) { + bb->last_ins->next = bbadd->first_ins; + bbadd->first_ins->prev = bb->last_ins; + bb->last_ins = bbadd->last_ins; + } + } else { + bb->first_ins = bbadd->first_ins; + bb->last_ins = bbadd->last_ins; + } + bb->next_bb = bbadd->next_bb; + + // Fixup bb links + bb->out_count = bbadd->out_count; + bb->out_bb = bbadd->out_bb; + for (int i = 0; i < bbadd->out_count; i++) { + for (int j = 0; j < bbadd->out_bb [i]->in_count; j++) { + if (bbadd->out_bb [i]->in_bb [j] == bbadd) + bbadd->out_bb [i]->in_bb [j] = bb; + } + } + + mark_bb_as_dead (td, bbadd, bb); +} + +// array must contain ref +static void +remove_bblock_ref (InterpBasicBlock **array, InterpBasicBlock *ref, int len) +{ + int i = 0; + while (array [i] != ref) + i++; + i++; + while (i < len) { + array [i - 1] = array [i]; + i++; + } +} + +static void +interp_unlink_bblocks (InterpBasicBlock *from, InterpBasicBlock *to) +{ + remove_bblock_ref (from->out_bb, to, from->out_count); + from->out_count--; + remove_bblock_ref (to->in_bb, from, to->in_count); + to->in_count--; +} + +static gboolean +interp_remove_bblock (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *prev_bb) +{ + gboolean needs_cprop = FALSE; + + for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (ins->opcode == MINT_LDLOCA_S) { + td->vars [ins->sregs [0]].indirects--; + if (!td->vars [ins->sregs [0]].indirects) { + // We can do cprop now through this local. Run cprop again. + needs_cprop = TRUE; + } + } + } + while (bb->in_count) + interp_unlink_bblocks (bb->in_bb [0], bb); + while (bb->out_count) + interp_unlink_bblocks (bb, bb->out_bb [0]); + prev_bb->next_bb = bb->next_bb; + mark_bb_as_dead (td, bb, bb->next_bb); + + return needs_cprop; +} + +void +interp_link_bblocks (TransformData *td, InterpBasicBlock *from, InterpBasicBlock *to) +{ + int i; + gboolean found = FALSE; + + for (i = 0; i < from->out_count; ++i) { + if (to == from->out_bb [i]) { + found = TRUE; + break; + } + } + if (!found) { + InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (from->out_count + 1)); + for (i = 0; i < from->out_count; ++i) + newa [i] = from->out_bb [i]; + newa [i] = to; + from->out_count++; + from->out_bb = newa; + } + + found = FALSE; + for (i = 0; i < to->in_count; ++i) { + if (from == to->in_bb [i]) { + found = TRUE; + break; + } + } + if (!found) { + InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (to->in_count + 1)); + for (i = 0; i < to->in_count; ++i) + newa [i] = to->in_bb [i]; + newa [i] = from; + to->in_count++; + to->in_bb = newa; + } +} + +static void +interp_mark_reachable_bblocks (TransformData *td) +{ + InterpBasicBlock **queue = mono_mempool_alloc0 (td->mempool, td->bb_count * sizeof (InterpBasicBlock*)); + InterpBasicBlock *current; + int cur_index = 0; + int next_position = 0; + + // FIXME There is no need to force eh bblocks to remain alive + current = td->entry_bb; + while (current != NULL) { + if (current->eh_block || current->patchpoint_data) { + queue [next_position++] = current; + current->reachable = TRUE; + } else { + current->reachable = FALSE; + } + current = current->next_bb; + } + + queue [next_position++] = td->entry_bb; + td->entry_bb->reachable = TRUE; + + // We have the roots, traverse everything else + while (cur_index < next_position) { + current = queue [cur_index++]; + for (int i = 0; i < current->out_count; i++) { + InterpBasicBlock *child = current->out_bb [i]; + if (!child->reachable) { + queue [next_position++] = child; + child->reachable = TRUE; + } + } + } +} + +/** + * Returns TRUE if instruction or previous instructions defines at least one of the variables, FALSE otherwise. + */ + +static gboolean +interp_prev_block_defines_var (InterpInst *ins, int var1, int var2) +{ + // Check max of 5 instructions + for (int i = 0; i < 5; i++) { + ins = interp_prev_ins (ins); + if (!ins) + return FALSE; + if (mono_interp_op_dregs [ins->opcode] && (ins->dreg == var1 || ins->dreg == var2)) + return TRUE; + } + return FALSE; +} + +/** + * Check if the given basic block has a known pattern for inlining into callers blocks, if so, return a pointer to the conditional branch instruction. + * + * The known patterns are: + * - `branch`: a conditional branch instruction. + * - `ldc; branch`: a load instruction followed by a binary conditional branch. + * - `ldc; compare; branch`: a load instruction followed by a compare instruction and a unary conditional branch. + */ +static InterpInst* +interp_inline_into_callers (InterpInst *first, int *lookup_var1, int *lookup_var2) { + // pattern `branch` + if (MINT_IS_CONDITIONAL_BRANCH (first->opcode)) { + *lookup_var1 = first->sregs [0]; + *lookup_var2 = (mono_interp_op_dregs [first->opcode] > 1) ? first->sregs [1] : -1; + return first; + } + + if (MINT_IS_LDC_I4 (first->opcode)) { + InterpInst *second = interp_next_ins (first); + if (!second) + return NULL; + *lookup_var2 = -1; + gboolean first_var_defined = first->dreg == second->sregs [0]; + gboolean second_var_defined = first->dreg == second->sregs [1]; + // pattern `ldc; binop conditional branch` + if (MINT_IS_BINOP_CONDITIONAL_BRANCH (second->opcode) && (first_var_defined || second_var_defined)) { + *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; + return second; + } + + InterpInst *third = interp_next_ins (second); + if (!third) + return NULL; + // pattern `ldc; compare; conditional branch` + if (MINT_IS_COMPARE (second->opcode) && (first_var_defined || second_var_defined) + && MINT_IS_UNOP_CONDITIONAL_BRANCH (third->opcode) && second->dreg == third->sregs [0]) { + *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; + return third; + } + } + + return NULL; +} + +InterpInst* +interp_first_ins (InterpBasicBlock *bb) +{ + InterpInst *ins = bb->first_ins; + if (!ins || !interp_ins_is_nop (ins)) + return ins; + while (ins && interp_ins_is_nop (ins)) + ins = ins->next; + return ins; +} + + +static InterpInst* +interp_last_ins (InterpBasicBlock *bb) +{ + InterpInst *ins = bb->last_ins; + if (!ins || !interp_ins_is_nop (ins)) + return ins; + return interp_prev_ins (ins); +} + +static void +interp_reorder_bblocks (TransformData *td) +{ + InterpBasicBlock *bb; + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + if (bb->eh_block) + continue; + InterpInst *first = interp_first_ins (bb); + if (!first) + continue; + int lookup_var1, lookup_var2; + InterpInst *cond_ins = interp_inline_into_callers (first, &lookup_var1, &lookup_var2); + if (cond_ins) { + // This means this bblock match a pattern for inlining into callers, with a conditional branch + int i = 0; + while (i < bb->in_count) { + InterpBasicBlock *in_bb = bb->in_bb [i]; + InterpInst *last_ins = interp_last_ins (in_bb); + if (last_ins && last_ins->opcode == MINT_BR && interp_prev_block_defines_var (last_ins, lookup_var1, lookup_var2)) { + // This bblock is reached unconditionally from one of its parents + // Move the conditional branch inside the parent to facilitate propagation + // of condition value. + InterpBasicBlock *cond_true_bb = cond_ins->info.target_bb; + InterpBasicBlock *next_bb = bb->next_bb; + + // Parent bb will do the conditional branch + interp_unlink_bblocks (in_bb, bb); + // Remove ending MINT_BR + interp_clear_ins (last_ins); + // Copy all instructions one by one, from interp_first_ins (bb) to the end of the in_bb + InterpInst *copy_ins = first; + while (copy_ins) { + InterpInst *new_ins = interp_insert_ins_bb (td, in_bb, in_bb->last_ins, copy_ins->opcode); + new_ins->dreg = copy_ins->dreg; + new_ins->sregs [0] = copy_ins->sregs [0]; + if (mono_interp_op_sregs [copy_ins->opcode] > 1) + new_ins->sregs [1] = copy_ins->sregs [1]; + + new_ins->data [0] = copy_ins->data [0]; + if (copy_ins->opcode == MINT_LDC_I4) + new_ins->data [1] = copy_ins->data [1]; + + copy_ins = interp_next_ins (copy_ins); + } + in_bb->last_ins->info.target_bb = cond_true_bb; + interp_link_bblocks (td, in_bb, cond_true_bb); + + // Create new fallthrough bb between in_bb and in_bb->next_bb + InterpBasicBlock *new_bb = interp_alloc_bb (td); + new_bb->next_bb = in_bb->next_bb; + in_bb->next_bb = new_bb; + new_bb->il_offset = in_bb->il_offset; + interp_link_bblocks (td, in_bb, new_bb); + + InterpInst *new_inst = interp_insert_ins_bb (td, new_bb, NULL, MINT_BR); + new_inst->info.target_bb = next_bb; + + interp_link_bblocks (td, new_bb, next_bb); + if (td->verbose_level) { + GString* bb_info = interp_get_bb_links (bb); + GString* in_bb_info = interp_get_bb_links (in_bb); + GString* new_bb_info = interp_get_bb_links (new_bb); + g_print ("Moved cond branch BB%d into BB%d, new BB%d\n", bb->index, in_bb->index, new_bb->index); + g_print ("\tBB%d: %s\n", bb->index, bb_info->str); + g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); + g_print ("\tBB%d: %s\n", new_bb->index, new_bb_info->str); + g_string_free (bb_info, TRUE); + g_string_free (in_bb_info, TRUE); + g_string_free (new_bb_info, TRUE); + } + // Since we changed links, in_bb might have changed, loop again from the start + i = 0; + } else { + i++; + } + } + } else if (first->opcode == MINT_BR) { + // All bblocks jumping into this bblock can jump directly into the br target since it is the single instruction of the bb + int i = 0; + while (i < bb->in_count) { + InterpBasicBlock *in_bb = bb->in_bb [i]; + InterpInst *last_ins = interp_last_ins (in_bb); + if (last_ins && (MINT_IS_CONDITIONAL_BRANCH (last_ins->opcode) || + MINT_IS_UNCONDITIONAL_BRANCH (last_ins->opcode)) && + last_ins->info.target_bb == bb) { + InterpBasicBlock *target_bb = first->info.target_bb; + last_ins->info.target_bb = target_bb; + interp_unlink_bblocks (in_bb, bb); + interp_link_bblocks (td, in_bb, target_bb); + if (td->verbose_level) { + GString* bb_info = interp_get_bb_links (bb); + GString* in_bb_info = interp_get_bb_links (in_bb); + GString* target_bb_info = interp_get_bb_links (target_bb); + g_print ("Propagated target bb BB%d into BB%d\n", target_bb->index, in_bb->index); + g_print ("\tBB%d: %s\n", bb->index, bb_info->str); + g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); + g_print ("\tBB%d: %s\n", target_bb->index, target_bb_info->str); + g_string_free (bb_info, TRUE); + g_string_free (in_bb_info, TRUE); + g_string_free (target_bb_info, TRUE); + } + i = 0; + } else { + i++; + } + } + } + } +} + +// Traverse the list of basic blocks and merge adjacent blocks +static gboolean +interp_optimize_bblocks (TransformData *td) +{ + InterpBasicBlock *bb = td->entry_bb; + gboolean needs_cprop = FALSE; + + interp_reorder_bblocks (td); + + interp_mark_reachable_bblocks (td); + + while (TRUE) { + InterpBasicBlock *next_bb = bb->next_bb; + if (!next_bb) + break; + if (!next_bb->reachable) { + if (td->verbose_level) + g_print ("Removed BB%d\n", next_bb->index); + needs_cprop |= interp_remove_bblock (td, next_bb, bb); + continue; + } else if (bb->out_count == 1 && bb->out_bb [0] == next_bb && next_bb->in_count == 1 && !next_bb->eh_block && !next_bb->patchpoint_data) { + g_assert (next_bb->in_bb [0] == bb); + interp_merge_bblocks (td, bb, next_bb); + if (td->verbose_level) + g_print ("Merged BB%d and BB%d\n", bb->index, next_bb->index); + needs_cprop = TRUE; + continue; + } + + bb = next_bb; + } + return needs_cprop; +} + +static gboolean +interp_local_deadce (TransformData *td) +{ + int *local_ref_count = td->local_ref_count; + gboolean needs_dce = FALSE; + gboolean needs_cprop = FALSE; + + for (unsigned int i = 0; i < td->vars_size; i++) { + g_assert (local_ref_count [i] >= 0); + g_assert (td->vars [i].indirects >= 0); + if (td->vars [i].indirects || td->vars [i].dead) + continue; + if (!local_ref_count [i]) { + needs_dce = TRUE; + td->vars [i].dead = TRUE; + } else if (!td->vars [i].unknown_use) { + if (!td->vars [i].local_only) { + // The value of this var is not passed between multiple basic blocks + td->vars [i].local_only = TRUE; + if (td->verbose_level) + g_print ("Var %d is local only\n", i); + needs_cprop = TRUE; + } + } + td->vars [i].unknown_use = FALSE; + } + + // Return early if all locals are alive + if (!needs_dce) + return needs_cprop; + + // Kill instructions that don't use stack and are storing into dead locals + for (InterpBasicBlock *bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (MINT_NO_SIDE_EFFECTS (ins->opcode) || + ins->opcode == MINT_LDLOCA_S) { + int dreg = ins->dreg; + if (td->vars [dreg].dead) { + if (td->verbose_level) { + g_print ("kill dead ins:\n\t"); + interp_dump_ins (ins, td->data_items); + } + + if (ins->opcode == MINT_LDLOCA_S) { + td->vars [ins->sregs [0]].indirects--; + if (!td->vars [ins->sregs [0]].indirects) { + // We can do cprop now through this local. Run cprop again. + needs_cprop = TRUE; + } + } + interp_clear_ins (ins); + // FIXME This is lazy. We should update the ref count for the sregs and redo deadce. + needs_cprop = TRUE; + } + } + } + } + return needs_cprop; +} + +static InterpInst* +interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct) +{ + int size = mono_interp_oplen [ins->opcode]; + int dreg = ins->dreg; + + if (size < 5) { + ins = interp_insert_ins (td, ins, MINT_LDC_I8); + interp_clear_ins (ins->prev); + } else { + ins->opcode = MINT_LDC_I8; + } + WRITE64_INS (ins, 0, &ct); + ins->dreg = dreg; + + return ins; +} + +static gint64 +interp_get_const_from_ldc_i8 (InterpInst *ins) +{ + switch (ins->opcode) { + case MINT_LDC_I8_0: return 0; + case MINT_LDC_I8_S: return (gint64)(gint16)ins->data [0]; + case MINT_LDC_I8: return READ64 (&ins->data [0]); + default: + g_assert_not_reached (); + } +} + +static int +interp_get_mt_for_ldind (int ldind_op) +{ + switch (ldind_op) { + case MINT_LDIND_I1: return MINT_TYPE_I1; + case MINT_LDIND_U1: return MINT_TYPE_U1; + case MINT_LDIND_I2: return MINT_TYPE_I2; + case MINT_LDIND_U2: return MINT_TYPE_U2; + case MINT_LDIND_I4: return MINT_TYPE_I4; + case MINT_LDIND_I8: return MINT_TYPE_I8; + case MINT_LDIND_R4: return MINT_TYPE_R4; + case MINT_LDIND_R8: return MINT_TYPE_R8; + default: + g_assert_not_reached (); + } + return -1; +} + +#define INTERP_FOLD_UNOP(opcode,val_type,field,op) \ + case opcode: \ + result.type = val_type; \ + result.field = op val->field; \ + break; + +#define INTERP_FOLD_CONV(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type) \ + case opcode: \ + result.type = val_type_dst; \ + result.field_dst = (cast_type)val->field_src; \ + break; + +#define INTERP_FOLD_CONV_FULL(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type,cond) \ + case opcode: \ + if (!(cond)) return ins; \ + result.type = val_type_dst; \ + result.field_dst = (cast_type)val->field_src; \ + break; + +static InterpInst* +interp_fold_unop (TransformData *td, InterpVarValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + // ins should be an unop, therefore it should have a single dreg and a single sreg + int dreg = ins->dreg; + int sreg = ins->sregs [0]; + InterpVarValue *val = &local_defs [sreg]; + InterpVarValue result; + + if (val->type != VAR_VALUE_I4 && val->type != VAR_VALUE_I8) + return ins; + + // Top of the stack is a constant + switch (ins->opcode) { + INTERP_FOLD_UNOP (MINT_ADD1_I4, VAR_VALUE_I4, i, 1+); + INTERP_FOLD_UNOP (MINT_ADD1_I8, VAR_VALUE_I8, l, 1+); + INTERP_FOLD_UNOP (MINT_SUB1_I4, VAR_VALUE_I4, i, -1+); + INTERP_FOLD_UNOP (MINT_SUB1_I8, VAR_VALUE_I8, l, -1+); + INTERP_FOLD_UNOP (MINT_NEG_I4, VAR_VALUE_I4, i, -); + INTERP_FOLD_UNOP (MINT_NEG_I8, VAR_VALUE_I8, l, -); + INTERP_FOLD_UNOP (MINT_NOT_I4, VAR_VALUE_I4, i, ~); + INTERP_FOLD_UNOP (MINT_NOT_I8, VAR_VALUE_I8, l, ~); + INTERP_FOLD_UNOP (MINT_CEQ0_I4, VAR_VALUE_I4, i, 0 ==); + + INTERP_FOLD_CONV (MINT_CONV_I1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8); + INTERP_FOLD_CONV (MINT_CONV_I1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8); + INTERP_FOLD_CONV (MINT_CONV_U1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint8); + INTERP_FOLD_CONV (MINT_CONV_U1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint8); + + INTERP_FOLD_CONV (MINT_CONV_I2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint16); + INTERP_FOLD_CONV (MINT_CONV_I2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint16); + INTERP_FOLD_CONV (MINT_CONV_U2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint16); + INTERP_FOLD_CONV (MINT_CONV_U2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint16); + + INTERP_FOLD_CONV (MINT_CONV_I8_I4, VAR_VALUE_I8, l, VAR_VALUE_I4, i, gint32); + INTERP_FOLD_CONV (MINT_CONV_I8_U4, VAR_VALUE_I8, l, VAR_VALUE_I4, i, guint32); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8, val->i >= G_MININT8 && val->i <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8, val->l >= G_MININT8 && val->l <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8, val->i >= 0 && val->i <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8, val->l >= 0 && val->l <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint8, val->i >= 0 && val->i <= G_MAXUINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint8, val->l >= 0 && val->l <= G_MAXUINT8); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint16, val->i >= G_MININT16 && val->i <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, i, gint16, val->l >= G_MININT16 && val->l <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint16, val->i >= 0 && val->i <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint16, val->l >= 0 && val->l <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint16, val->i >= 0 && val->i <= G_MAXUINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint16, val->l >= 0 && val->l <= G_MAXUINT16); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint32, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint32, val->l >= G_MININT32 && val->l <= G_MAXINT32); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint32, val->l >= 0 && val->l <= G_MAXINT32); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint32, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint32, val->l >= 0 && val->l <= G_MAXINT32); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I8_U8, VAR_VALUE_I8, l, VAR_VALUE_I8, l, gint64, val->l >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I4, VAR_VALUE_I8, l, VAR_VALUE_I4, i, guint64, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I8, VAR_VALUE_I8, l, VAR_VALUE_I8, l, guint64, val->l >= 0); + + default: + return ins; + } + + // We were able to compute the result of the ins instruction. We replace the unop + // with a LDC of the constant. We leave alone the sregs of this instruction, for + // deadce to kill the instructions initializing them. + if (result.type == VAR_VALUE_I4) + ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); + else if (result.type == VAR_VALUE_I8) + ins = interp_inst_replace_with_i8_const (td, ins, result.l); + else + g_assert_not_reached (); + + if (td->verbose_level) { + g_print ("Fold unop :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_ref_count [sreg]--; + result.ins = ins; + result.ref_count = 0; + local_defs [dreg] = result; + + return ins; +} + +#define INTERP_FOLD_UNOP_BR(_opcode,_cond) \ + case _opcode: \ + if (_cond) { \ + ins->opcode = MINT_BR; \ + if (cbb->next_bb != ins->info.target_bb) \ + interp_unlink_bblocks (cbb, cbb->next_bb); \ + for (InterpInst *it = ins->next; it != NULL; it = it->next) \ + interp_clear_ins (it); \ + } else { \ + interp_clear_ins (ins); \ + interp_unlink_bblocks (cbb, ins->info.target_bb); \ + } \ + break; + +static InterpInst* +interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, InterpVarValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + // ins should be an unop conditional branch, therefore it should have a single sreg + int sreg = ins->sregs [0]; + InterpVarValue *val = &local_defs [sreg]; + + if (val->type != VAR_VALUE_I4 && val->type != VAR_VALUE_I8 && val->type != VAR_VALUE_NON_NULL) + return ins; + + if (val->type == VAR_VALUE_NON_NULL) { + switch (ins->opcode) { + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, FALSE); + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, FALSE); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, TRUE); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, TRUE); + + default: + return ins; + } + } else { + // Top of the stack is a constant + switch (ins->opcode) { + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, val->i == 0); + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, val->l == 0); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, val->i != 0); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, val->l != 0); + + default: + return ins; + } + } + + if (td->verbose_level) { + g_print ("Fold unop cond br :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_ref_count [sreg]--; + return ins; +} + +#define INTERP_FOLD_BINOP(opcode,local_type,field,op) \ + case opcode: \ + result.type = local_type; \ + result.field = val1->field op val2->field; \ + break; + +#define INTERP_FOLD_BINOP_FULL(opcode,local_type,field,op,cast_type,cond) \ + case opcode: \ + if (!(cond)) return ins; \ + result.type = local_type; \ + result.field = (cast_type)val1->field op (cast_type)val2->field; \ + break; + +#define INTERP_FOLD_SHIFTOP(opcode,local_type,field,shift_op,cast_type) \ + case opcode: \ + result.type = local_type; \ + result.field = (cast_type)val1->field shift_op val2->i; \ + break; + +#define INTERP_FOLD_RELOP(opcode,local_type,field,relop,cast_type) \ + case opcode: \ + result.type = VAR_VALUE_I4; \ + result.i = (cast_type) val1->field relop (cast_type) val2->field; \ + break; + + +static InterpInst* +interp_fold_binop (TransformData *td, InterpVarValue *local_defs, InterpInst *ins, gboolean *folded) +{ + int *local_ref_count = td->local_ref_count; + // ins should be a binop, therefore it should have a single dreg and two sregs + int dreg = ins->dreg; + int sreg1 = ins->sregs [0]; + int sreg2 = ins->sregs [1]; + InterpVarValue *val1 = &local_defs [sreg1]; + InterpVarValue *val2 = &local_defs [sreg2]; + InterpVarValue result; + + *folded = FALSE; + + if (val1->type != VAR_VALUE_I4 && val1->type != VAR_VALUE_I8) + return ins; + if (val2->type != VAR_VALUE_I4 && val2->type != VAR_VALUE_I8) + return ins; + + // Top two values of the stack are constants + switch (ins->opcode) { + INTERP_FOLD_BINOP (MINT_ADD_I4, VAR_VALUE_I4, i, +); + INTERP_FOLD_BINOP (MINT_ADD_I8, VAR_VALUE_I8, l, +); + INTERP_FOLD_BINOP (MINT_SUB_I4, VAR_VALUE_I4, i, -); + INTERP_FOLD_BINOP (MINT_SUB_I8, VAR_VALUE_I8, l, -); + INTERP_FOLD_BINOP (MINT_MUL_I4, VAR_VALUE_I4, i, *); + INTERP_FOLD_BINOP (MINT_MUL_I8, VAR_VALUE_I8, l, *); + + INTERP_FOLD_BINOP (MINT_AND_I4, VAR_VALUE_I4, i, &); + INTERP_FOLD_BINOP (MINT_AND_I8, VAR_VALUE_I8, l, &); + INTERP_FOLD_BINOP (MINT_OR_I4, VAR_VALUE_I4, i, |); + INTERP_FOLD_BINOP (MINT_OR_I8, VAR_VALUE_I8, l, |); + INTERP_FOLD_BINOP (MINT_XOR_I4, VAR_VALUE_I4, i, ^); + INTERP_FOLD_BINOP (MINT_XOR_I8, VAR_VALUE_I8, l, ^); + + INTERP_FOLD_SHIFTOP (MINT_SHL_I4, VAR_VALUE_I4, i, <<, gint32); + INTERP_FOLD_SHIFTOP (MINT_SHL_I8, VAR_VALUE_I8, l, <<, gint64); + INTERP_FOLD_SHIFTOP (MINT_SHR_I4, VAR_VALUE_I4, i, >>, gint32); + INTERP_FOLD_SHIFTOP (MINT_SHR_I8, VAR_VALUE_I8, l, >>, gint64); + INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, VAR_VALUE_I4, i, >>, guint32); + INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, VAR_VALUE_I8, l, >>, guint64); + + INTERP_FOLD_RELOP (MINT_CEQ_I4, VAR_VALUE_I4, i, ==, gint32); + INTERP_FOLD_RELOP (MINT_CEQ_I8, VAR_VALUE_I8, l, ==, gint64); + INTERP_FOLD_RELOP (MINT_CNE_I4, VAR_VALUE_I4, i, !=, gint32); + INTERP_FOLD_RELOP (MINT_CNE_I8, VAR_VALUE_I8, l, !=, gint64); + + INTERP_FOLD_RELOP (MINT_CGT_I4, VAR_VALUE_I4, i, >, gint32); + INTERP_FOLD_RELOP (MINT_CGT_I8, VAR_VALUE_I8, l, >, gint64); + INTERP_FOLD_RELOP (MINT_CGT_UN_I4, VAR_VALUE_I4, i, >, guint32); + INTERP_FOLD_RELOP (MINT_CGT_UN_I8, VAR_VALUE_I8, l, >, guint64); + + INTERP_FOLD_RELOP (MINT_CGE_I4, VAR_VALUE_I4, i, >=, gint32); + INTERP_FOLD_RELOP (MINT_CGE_I8, VAR_VALUE_I8, l, >=, gint64); + INTERP_FOLD_RELOP (MINT_CGE_UN_I4, VAR_VALUE_I4, i, >=, guint32); + INTERP_FOLD_RELOP (MINT_CGE_UN_I8, VAR_VALUE_I8, l, >=, guint64); + + INTERP_FOLD_RELOP (MINT_CLT_I4, VAR_VALUE_I4, i, <, gint32); + INTERP_FOLD_RELOP (MINT_CLT_I8, VAR_VALUE_I8, l, <, gint64); + INTERP_FOLD_RELOP (MINT_CLT_UN_I4, VAR_VALUE_I4, i, <, guint32); + INTERP_FOLD_RELOP (MINT_CLT_UN_I8, VAR_VALUE_I8, l, <, guint64); + + INTERP_FOLD_RELOP (MINT_CLE_I4, VAR_VALUE_I4, i, <=, gint32); + INTERP_FOLD_RELOP (MINT_CLE_I8, VAR_VALUE_I8, l, <=, gint64); + INTERP_FOLD_RELOP (MINT_CLE_UN_I4, VAR_VALUE_I4, i, <=, guint32); + INTERP_FOLD_RELOP (MINT_CLE_UN_I8, VAR_VALUE_I8, l, <=, guint64); + + INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, VAR_VALUE_I4, i, /, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); + INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, VAR_VALUE_I8, l, /, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); + INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, VAR_VALUE_I4, i, /, guint32, val2->i != 0); + INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, VAR_VALUE_I8, l, /, guint64, val2->l != 0); + + INTERP_FOLD_BINOP_FULL (MINT_REM_I4, VAR_VALUE_I4, i, %, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); + INTERP_FOLD_BINOP_FULL (MINT_REM_I8, VAR_VALUE_I8, l, %, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); + INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, VAR_VALUE_I4, i, %, guint32, val2->i != 0); + INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, VAR_VALUE_I8, l, %, guint64, val2->l != 0); + + default: + return ins; + } + + // We were able to compute the result of the ins instruction. We replace the binop + // with a LDC of the constant. We leave alone the sregs of this instruction, for + // deadce to kill the instructions initializing them. + *folded = TRUE; + if (result.type == VAR_VALUE_I4) + ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); + else if (result.type == VAR_VALUE_I8) + ins = interp_inst_replace_with_i8_const (td, ins, result.l); + else + g_assert_not_reached (); + + if (td->verbose_level) { + g_print ("Fold binop :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_ref_count [sreg1]--; + local_ref_count [sreg2]--; + result.ins = ins; + result.ref_count = 0; + local_defs [dreg] = result; + return ins; +} + +// Due to poor current design, the branch op might not be the last instruction in the bblock +// (in case we fallthrough and need to have the stack locals match the ones from next_bb, done +// in fixup_newbb_stack_locals). If that's the case, clear all these mov's. This helps bblock +// merging quickly find the MINT_BR opcode. +#define INTERP_FOLD_BINOP_BR(_opcode,_local_type,_cond) \ + case _opcode: \ + if (_cond) { \ + ins->opcode = MINT_BR; \ + if (cbb->next_bb != ins->info.target_bb) \ + interp_unlink_bblocks (cbb, cbb->next_bb); \ + for (InterpInst *it = ins->next; it != NULL; it = it->next) \ + interp_clear_ins (it); \ + } else { \ + interp_clear_ins (ins); \ + interp_unlink_bblocks (cbb, ins->info.target_bb); \ + } \ + break; + +static InterpInst* +interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, InterpVarValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + // ins should be a conditional binop, therefore it should have only two sregs + int sreg1 = ins->sregs [0]; + int sreg2 = ins->sregs [1]; + InterpVarValue *val1 = &local_defs [sreg1]; + InterpVarValue *val2 = &local_defs [sreg2]; + + if (val1->type != VAR_VALUE_I4 && val1->type != VAR_VALUE_I8) + return ins; + if (val2->type != VAR_VALUE_I4 && val2->type != VAR_VALUE_I8) + return ins; + + switch (ins->opcode) { + INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, VAR_VALUE_I4, val1->i == val2->i); + INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, VAR_VALUE_I8, val1->l == val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_I4, VAR_VALUE_I4, val1->i >= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_I8, VAR_VALUE_I8, val1->l >= val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_I4, VAR_VALUE_I4, val1->i > val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_I8, VAR_VALUE_I8, val1->l > val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_I4, VAR_VALUE_I4, val1->i < val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_I8, VAR_VALUE_I8, val1->l < val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_I4, VAR_VALUE_I4, val1->i <= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_I8, VAR_VALUE_I8, val1->l <= val2->l); + + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, VAR_VALUE_I4, val1->i != val2->i); + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, VAR_VALUE_I8, val1->l != val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, VAR_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, VAR_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, VAR_VALUE_I4, (guint32)val1->i > (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, VAR_VALUE_I8, (guint64)val1->l > (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, VAR_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, VAR_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, VAR_VALUE_I4, (guint32)val1->i < (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, VAR_VALUE_I8, (guint64)val1->l < (guint64)val2->l); + + default: + return ins; + } + if (td->verbose_level) { + g_print ("Fold binop cond br :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_ref_count [sreg1]--; + local_ref_count [sreg2]--; + return ins; +} + +static void +write_v128_element (gpointer v128_addr, InterpVarValue *val, int index, int el_size) +{ + gpointer el_addr = (gint8*)v128_addr + index * el_size; + g_assert ((gint8*)el_addr < ((gint8*)v128_addr + 16)); + switch (el_size) { + case 1: *(gint8*)el_addr = (gint8)val->i; break; + case 2: *(gint16*)el_addr = (gint16)val->i; break; + case 4: *(gint32*)el_addr = val->i; break; // this also handles r4 + case 8: *(gint64*)el_addr = val->l; break; + default: + g_assert_not_reached (); + } +} + +static InterpInst* +interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, InterpVarValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + + int *args = ins->info.call_info->call_args; + int index = 0; + int var = args [index]; + while (var != -1) { + InterpVarValue *val = &local_defs [var]; + if (val->type != VAR_VALUE_I4 && val->type != VAR_VALUE_I8 && val->type != VAR_VALUE_R4) + return ins; + index++; + var = args [index]; + } + + // If we reached this point, it means that all args of the simd_create are constants + // We can replace the simd_create with simd_ldc + int el_size = 16 / index; + int dreg = ins->dreg; + + ins = interp_insert_ins (td, ins, MINT_SIMD_V128_LDC); + interp_clear_ins (ins->prev); + interp_ins_set_dreg (ins, dreg); + + gpointer v128_addr = &ins->data [0]; + + index = 0; + var = args [index]; + while (var != -1) { + InterpVarValue *val = &local_defs [var]; + write_v128_element (v128_addr, val, index, el_size); + val->ref_count--; + local_ref_count [var]--; + index++; + var = args [index]; + } + + if (td->verbose_level) { + g_print ("Fold simd create:\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_defs [dreg].ins = ins; + local_defs [dreg].type = VAR_VALUE_NONE; + + return ins; +} + +static void +cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, InterpVarValue *local_defs) +{ + int *local_ref_count = td->local_ref_count; + int sreg = *psreg; + + local_ref_count [sreg]++; + local_defs [sreg].ref_count++; + if (local_defs [sreg].type == VAR_VALUE_OTHER_VAR) { + int cprop_local = local_defs [sreg].var; + + // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been + // modified, so we can't use it. + if (local_defs [cprop_local].ins != NULL && local_defs [cprop_local].def_index > local_defs [sreg].def_index) + return; + + if (td->verbose_level) + g_print ("cprop %d -> %d:\n\t", sreg, cprop_local); + local_ref_count [sreg]--; + *psreg = cprop_local; + local_ref_count [cprop_local]++; + if (td->verbose_level) + interp_dump_ins (ins, td->data_items); + } else if (!local_defs [sreg].ins) { + td->vars [sreg].unknown_use = TRUE; + } +} + +static void +clear_local_defs (TransformData *td, int *pvar, void *data) +{ + int var = *pvar; + InterpVarValue *local_defs = (InterpVarValue*) data; + local_defs [var].type = VAR_VALUE_NONE; + local_defs [var].ins = NULL; + local_defs [var].ref_count = 0; +} + +static void +clear_unused_defs (TransformData *td, int *pvar, void *data) +{ + int var = *pvar; + if (!td->vars [var].local_only) + return; + if (td->vars [var].indirects) + return; + + InterpVarValue *local_def = &((InterpVarValue*) data) [var]; + InterpInst *def_ins = local_def->ins; + if (!def_ins) + return; + if (local_def->ref_count) + return; + + // This is a local only var that is defined in this bblock and its value is not used + // at all in this bblock. Clear the definition + if (MINT_NO_SIDE_EFFECTS (def_ins->opcode)) { + for (int i = 0; i < mono_interp_op_sregs [def_ins->opcode]; i++) + td->local_ref_count [def_ins->sregs [i]]--; + if (td->verbose_level) { + g_print ("kill unused local def:\n\t"); + interp_dump_ins (def_ins, td->data_items); + } + interp_clear_ins (def_ins); + } +} + +static void +interp_cprop (TransformData *td) +{ + InterpVarValue *local_defs = (InterpVarValue*) g_malloc (td->vars_size * sizeof (InterpVarValue)); + int *local_ref_count = (int*) g_malloc (td->vars_size * sizeof (int)); + InterpBasicBlock *bb; + gboolean needs_retry; + int ins_index; + int iteration_count = 0; + + td->local_ref_count = local_ref_count; +retry: + needs_retry = FALSE; + memset (local_ref_count, 0, td->vars_size * sizeof (int)); + + if (td->verbose_level) + g_print ("\ncprop iteration %d\n", iteration_count++); + + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + ins_index = 0; + + // Set cbb since we do some instruction inserting below + td->cbb = bb; + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) + interp_foreach_ins_var (td, ins, local_defs, clear_local_defs); + + if (td->verbose_level) { + GString* bb_info = interp_get_bb_links (bb); + g_print ("\nBB%d: %s\n", bb->index, bb_info->str); + g_string_free (bb_info, TRUE); + } + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + + if (opcode == MINT_NOP) + continue; + + int num_sregs = mono_interp_op_sregs [opcode]; + int num_dregs = mono_interp_op_dregs [opcode]; + gint32 *sregs = &ins->sregs [0]; + gint32 dreg = ins->dreg; + + if (td->verbose_level && ins->opcode != MINT_NOP && ins->opcode != MINT_IL_SEQ_POINT) + interp_dump_ins (ins, td->data_items); + + for (int i = 0; i < num_sregs; i++) { + if (sregs [i] == MINT_CALL_ARGS_SREG) { + if (ins->info.call_info && ins->info.call_info->call_args) { + int *call_args = ins->info.call_info->call_args; + while (*call_args != -1) { + cprop_sreg (td, ins, call_args, local_defs); + call_args++; + } + } + } else { + cprop_sreg (td, ins, &sregs [i], local_defs); + } + } + + if (num_dregs) { + // Check if the previous definition of this var was used at all. + // If it wasn't we can just clear the instruction + // + // MINT_MOV_DST_OFF doesn't fully write to the var, so we special case it here + if (local_defs [dreg].ins != NULL && + local_defs [dreg].ref_count == 0 && + !td->vars [dreg].indirects && + opcode != MINT_MOV_DST_OFF) { + InterpInst *prev_def = local_defs [dreg].ins; + if (MINT_NO_SIDE_EFFECTS (prev_def->opcode)) { + for (int i = 0; i < mono_interp_op_sregs [prev_def->opcode]; i++) + local_ref_count [prev_def->sregs [i]]--; + interp_clear_ins (prev_def); + } + } + local_defs [dreg].type = VAR_VALUE_NONE; + local_defs [dreg].ins = ins; + local_defs [dreg].def_index = ins_index; + } + + // We always store to the full i4, except as part of STIND opcodes. These opcodes can be + // applied to a local var only if that var has LDLOCA applied to it + if ((opcode >= MINT_MOV_I4_I1 && opcode <= MINT_MOV_I4_U2) && !td->vars [sregs [0]].indirects) { + ins->opcode = MINT_MOV_4; + opcode = MINT_MOV_4; + } + + if (opcode == MINT_MOV_4 || opcode == MINT_MOV_8 || opcode == MINT_MOV_VT) { + int sreg = sregs [0]; + if (dreg == sreg) { + if (td->verbose_level) + g_print ("clear redundant mov\n"); + interp_clear_ins (ins); + local_ref_count [sreg]--; + } else if (td->vars [sreg].indirects || td->vars [dreg].indirects) { + // Don't bother with indirect locals + } else if (local_defs [sreg].type == VAR_VALUE_I4 || local_defs [sreg].type == VAR_VALUE_I8) { + // Replace mov with ldc + gboolean is_i4 = local_defs [sreg].type == VAR_VALUE_I4; + g_assert (!td->vars [sreg].indirects); + local_defs [dreg].type = local_defs [sreg].type; + if (is_i4) { + int ct = local_defs [sreg].i; + ins = interp_get_ldc_i4_from_const (td, ins, ct, dreg); + local_defs [dreg].i = ct; + } else { + gint64 ct = local_defs [sreg].l; + ins = interp_inst_replace_with_i8_const (td, ins, ct); + local_defs [dreg].l = ct; + } + local_defs [dreg].ins = ins; + local_ref_count [sreg]--; + if (td->verbose_level) { + g_print ("cprop loc %d -> ct :\n\t", sreg); + interp_dump_ins (ins, td->data_items); + } + } else if (local_defs [sreg].ins != NULL && + td->vars [sreg].execution_stack && + !td->vars [dreg].execution_stack && + interp_prev_ins (ins) == local_defs [sreg].ins && + !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { + // hackish temporary optimization that won't be necessary in the future + // We replace `local1 <- ?, local2 <- local1` with `local2 <- ?, local1 <- local2` + // if local1 is execution stack local and local2 is normal global local. This makes + // it more likely for `local1 <- local2` to be killed, while before we always needed + // to store to the global local, which is likely accessed by other instructions. + InterpInst *def = local_defs [sreg].ins; + int original_dreg = def->dreg; + + def->dreg = dreg; + ins->dreg = original_dreg; + sregs [0] = dreg; + + local_defs [dreg].type = VAR_VALUE_NONE; + local_defs [dreg].ins = def; + local_defs [dreg].def_index = local_defs [original_dreg].def_index; + local_defs [dreg].ref_count++; + local_defs [original_dreg].type = VAR_VALUE_OTHER_VAR; + local_defs [original_dreg].ins = ins; + local_defs [original_dreg].var = dreg; + local_defs [original_dreg].def_index = ins_index; + local_defs [original_dreg].ref_count--; + + local_ref_count [original_dreg]--; + local_ref_count [dreg]++; + + if (td->verbose_level) { + g_print ("cprop dreg:\n\t"); + interp_dump_ins (def, td->data_items); + g_print ("\t"); + interp_dump_ins (ins, td->data_items); + } + } else { + if (td->verbose_level) + g_print ("local copy %d <- %d\n", dreg, sreg); + local_defs [dreg].type = VAR_VALUE_OTHER_VAR; + local_defs [dreg].var = sreg; + } + } else if (opcode == MINT_LDLOCA_S) { + // The local that we are taking the address of is not a sreg but still referenced + local_ref_count [ins->sregs [0]]++; + } else if (MINT_IS_LDC_I4 (opcode)) { + local_defs [dreg].type = VAR_VALUE_I4; + local_defs [dreg].i = interp_get_const_from_ldc_i4 (ins); + } else if (MINT_IS_LDC_I8 (opcode)) { + local_defs [dreg].type = VAR_VALUE_I8; + local_defs [dreg].l = interp_get_const_from_ldc_i8 (ins); + } else if (opcode == MINT_LDC_R4) { + guint32 val_u = READ32 (&ins->data [0]); + float f = *(float*)(&val_u); + local_defs [dreg].type = VAR_VALUE_R4; + local_defs [dreg].f = f; + } else if (ins->opcode == MINT_LDPTR) { +#if SIZEOF_VOID_P == 8 + local_defs [dreg].type = VAR_VALUE_I8; + local_defs [dreg].l = (gint64)td->data_items [ins->data [0]]; +#else + local_defs [dreg].type = VAR_VALUE_I4; + local_defs [dreg].i = (gint32)td->data_items [ins->data [0]]; +#endif + } else if (MINT_IS_UNOP (opcode)) { + ins = interp_fold_unop (td, local_defs, ins); + } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { + ins = interp_fold_unop_cond_br (td, bb, local_defs, ins); + } else if (MINT_IS_SIMD_CREATE (opcode)) { + ins = interp_fold_simd_create (td, bb, local_defs, ins); + } else if (MINT_IS_BINOP (opcode)) { + gboolean folded; + ins = interp_fold_binop (td, local_defs, ins, &folded); + if (!folded) { + int sreg = -1; + guint16 mov_op = 0; + if ((opcode == MINT_MUL_I4 || opcode == MINT_DIV_I4) && + local_defs [ins->sregs [1]].type == VAR_VALUE_I4 && + local_defs [ins->sregs [1]].i == 1) { + sreg = ins->sregs [0]; + mov_op = MINT_MOV_4; + } else if ((opcode == MINT_MUL_I8 || opcode == MINT_DIV_I8) && + local_defs [ins->sregs [1]].type == VAR_VALUE_I8 && + local_defs [ins->sregs [1]].l == 1) { + sreg = ins->sregs [0]; + mov_op = MINT_MOV_8; + } else if (opcode == MINT_MUL_I4 && + local_defs [ins->sregs [0]].type == VAR_VALUE_I4 && + local_defs [ins->sregs [0]].i == 1) { + sreg = ins->sregs [1]; + mov_op = MINT_MOV_4; + } else if (opcode == MINT_MUL_I8 && + local_defs [ins->sregs [0]].type == VAR_VALUE_I8 && + local_defs [ins->sregs [0]].l == 1) { + sreg = ins->sregs [1]; + mov_op = MINT_MOV_8; + } + if (sreg != -1) { + ins->opcode = mov_op; + ins->sregs [0] = sreg; + if (td->verbose_level) { + g_print ("Replace idempotent binop :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } + } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { + ins = interp_fold_binop_cond_br (td, bb, local_defs, ins); + } else if (MINT_IS_LDIND (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int local = ldloca->sregs [0]; + int mt = td->vars [local].mt; + if (mt != MINT_TYPE_VT) { + // LDIND cannot simply be replaced with a mov because it might also include a + // necessary conversion (especially when we do cprop and do moves between vars of + // different types). + int ldind_mt = interp_get_mt_for_ldind (opcode); + switch (ldind_mt) { + case MINT_TYPE_I1: ins->opcode = MINT_CONV_I1_I4; break; + case MINT_TYPE_U1: ins->opcode = MINT_CONV_U1_I4; break; + case MINT_TYPE_I2: ins->opcode = MINT_CONV_I2_I4; break; + case MINT_TYPE_U2: ins->opcode = MINT_CONV_U2_I4; break; + default: + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (ldind_mt, FALSE)); + break; + } + local_ref_count [sregs [0]]--; + interp_ins_set_sreg (ins, local); + + if (td->verbose_level) { + g_print ("Replace ldloca/ldind pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } + } else if (MINT_IS_LDFLD (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int mt = ins->opcode - MINT_LDFLD_I1; + int local = ldloca->sregs [0]; + // Allow ldloca instruction to be killed + local_ref_count [sregs [0]]--; + if (td->vars [local].mt == (ins->opcode - MINT_LDFLD_I1) && ins->data [0] == 0) { + // Replace LDLOCA + LDFLD with LDLOC, when the loading field represents + // the entire local. This is the case with loading the only field of an + // IntPtr. We don't handle value type loads. + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, TRUE)); + // The dreg of the MOV is the same as the dreg of the LDFLD + sregs [0] = local; + } else { + // Add mov.src.off to load directly from the local var space without use of ldloca. + int foffset = ins->data [0]; + guint16 ldsize = 0; + if (mt == MINT_TYPE_VT) + ldsize = ins->data [1]; + + // This loads just a part of the local valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); + interp_ins_set_dreg (ins, ins->prev->dreg); + interp_ins_set_sreg (ins, local); + ins->data [0] = GINT_TO_UINT16 (foffset); + ins->data [1] = GINT_TO_UINT16 (mt); + if (mt == MINT_TYPE_VT) + ins->data [2] = ldsize; + + interp_clear_ins (ins->prev); + } + + if (td->verbose_level) { + g_print ("Replace ldloca/ldfld pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } else if (opcode == MINT_INITOBJ) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int size = ins->data [0]; + int local = ldloca->sregs [0]; + // Replace LDLOCA + INITOBJ with or LDC + if (size <= 4) + ins->opcode = MINT_LDC_I4_0; + else if (size <= 8) + ins->opcode = MINT_LDC_I8_0; + else + ins->opcode = MINT_INITLOCAL; + local_ref_count [sregs [0]]--; + ins->dreg = local; + + if (td->verbose_level) { + g_print ("Replace ldloca/initobj pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } else if (opcode == MINT_LDOBJ_VT) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int ldsize = ins->data [0]; + int local = ldloca->sregs [0]; + local_ref_count [sregs [0]]--; + + if (ldsize == td->vars [local].size) { + // Replace LDLOCA + LDOBJ_VT with MOV_VT + ins->opcode = MINT_MOV_VT; + sregs [0] = local; + needs_retry = TRUE; + } else { + // This loads just a part of the local valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); + interp_ins_set_dreg (ins, ins->prev->dreg); + interp_ins_set_sreg (ins, local); + ins->data [0] = 0; + ins->data [1] = MINT_TYPE_VT; + ins->data [2] = GINT_TO_UINT16 (ldsize); + + interp_clear_ins (ins->prev); + } + if (td->verbose_level) { + g_print ("Replace ldloca/ldobj_vt pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + } + } else if (opcode == MINT_STOBJ_VT || opcode == MINT_STOBJ_VT_NOREF) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int stsize = ins->data [0]; + int local = ldloca->sregs [0]; + + if (stsize == td->vars [local].size) { + // Replace LDLOCA + STOBJ_VT with MOV_VT + local_ref_count [sregs [0]]--; + ins->opcode = MINT_MOV_VT; + sregs [0] = sregs [1]; + ins->dreg = local; + needs_retry = TRUE; + + if (td->verbose_level) { + g_print ("Replace ldloca/stobj_vt pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + } + } + } else if (MINT_IS_STIND (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int local = ldloca->sregs [0]; + int mt = td->vars [local].mt; + if (mt != MINT_TYPE_VT) { + // We have an 8 byte local, just replace the stind with a mov + local_ref_count [sregs [0]]--; + // We make the assumption that the STIND matches the local type + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, TRUE)); + interp_ins_set_dreg (ins, local); + interp_ins_set_sreg (ins, sregs [1]); + + if (td->verbose_level) { + g_print ("Replace ldloca/stind pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } + } else if (MINT_IS_STFLD (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int mt = ins->opcode - MINT_STFLD_I1; + int local = ldloca->sregs [0]; + local_ref_count [sregs [0]]--; + // Allow ldloca instruction to be killed + if (td->vars [local].mt == (ins->opcode - MINT_STFLD_I1) && ins->data [0] == 0) { + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, FALSE)); + // The sreg of the MOV is the same as the second sreg of the STFLD + ins->dreg = local; + sregs [0] = sregs [1]; + } else { + // Add mov.dst.off to store directly int the local var space without use of ldloca. + int foffset = ins->data [0]; + guint16 vtsize = 0; + if (mt == MINT_TYPE_VT) { + vtsize = ins->data [1]; + } +#ifdef NO_UNALIGNED_ACCESS + else { + // As with normal loads/stores we use memcpy for unaligned 8 byte accesses + if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && foffset % SIZEOF_VOID_P != 0) + vtsize = 8; + } +#endif + + // This stores just to part of the dest valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_DST_OFF); + interp_ins_set_dreg (ins, local); + interp_ins_set_sreg (ins, sregs [1]); + ins->data [0] = GINT_TO_UINT16 (foffset); + ins->data [1] = GINT_TO_UINT16 (mt); + ins->data [2] = vtsize; + + interp_clear_ins (ins->prev); + } + if (td->verbose_level) { + g_print ("Replace ldloca/stfld pair (off %p) :\n\t", (void *)(uintptr_t) ldloca->il_offset); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } else if (opcode == MINT_GETITEM_SPAN) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int local = ldloca->sregs [0]; + // Allow ldloca instruction to be killed + local_ref_count [sregs [0]]--; + // Instead of loading from the indirect pointer pass directly the vt var + ins->opcode = MINT_GETITEM_LOCALSPAN; + sregs [0] = local; + needs_retry = TRUE; + } + } else if (opcode == MINT_CKNULL) { + InterpInst *def = local_defs [sregs [0]].ins; + if (def && def->opcode == MINT_LDLOCA_S) { + // CKNULL on LDLOCA is a NOP + ins->opcode = MINT_MOV_P; + needs_retry = TRUE; + } + } else if (opcode == MINT_BOX) { + // TODO Add more relevant opcodes + local_defs [dreg].type = VAR_VALUE_NON_NULL; + } + + ins_index++; + } + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) + interp_foreach_ins_var (td, ins, local_defs, clear_unused_defs); + } + + needs_retry |= interp_local_deadce (td); + if (mono_interp_opt & INTERP_OPT_BBLOCKS) + needs_retry |= interp_optimize_bblocks (td); + + if (needs_retry) + goto retry; + + g_free (local_defs); +} + +void +mono_test_interp_cprop (TransformData *td) +{ + interp_cprop (td); +} + +static gboolean +get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) +{ + InterpInst *def = td->vars [sreg].def; + if (def != NULL && td->local_ref_count [sreg] == 1) { + gint64 ct; + if (MINT_IS_LDC_I4 (def->opcode)) + ct = interp_get_const_from_ldc_i4 (def); + else if (MINT_IS_LDC_I8 (def->opcode)) + ct = interp_get_const_from_ldc_i8 (def); + else + return FALSE; + gint64 min_val, max_val; + // We only propagate the immediate only if it fits into the desired type, + // so we don't accidentaly handle conversions wrong + switch (result_mt) { + case MINT_TYPE_I1: + min_val = G_MININT8; + max_val = G_MAXINT8; + break; + case MINT_TYPE_I2: + min_val = G_MININT16; + max_val = G_MAXINT16; + break; + case MINT_TYPE_U1: + min_val = 0; + max_val = G_MAXUINT8; + break; + case MINT_TYPE_U2: + min_val = 0; + max_val = G_MAXINT16; + break; + default: + g_assert_not_reached (); + + } + if (ct >= min_val && ct <= max_val) { + *imm = (gint16)ct; + return TRUE; + } + } + return FALSE; +} + +static int +get_binop_condbr_imm_sp (int opcode) +{ + switch (opcode) { + case MINT_BEQ_I4: return MINT_BEQ_I4_IMM_SP; + case MINT_BEQ_I8: return MINT_BEQ_I8_IMM_SP; + case MINT_BGE_I4: return MINT_BGE_I4_IMM_SP; + case MINT_BGE_I8: return MINT_BGE_I8_IMM_SP; + case MINT_BGT_I4: return MINT_BGT_I4_IMM_SP; + case MINT_BGT_I8: return MINT_BGT_I8_IMM_SP; + case MINT_BLT_I4: return MINT_BLT_I4_IMM_SP; + case MINT_BLT_I8: return MINT_BLT_I8_IMM_SP; + case MINT_BLE_I4: return MINT_BLE_I4_IMM_SP; + case MINT_BLE_I8: return MINT_BLE_I8_IMM_SP; + case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_IMM_SP; + case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_IMM_SP; + case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_IMM_SP; + case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_IMM_SP; + case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_IMM_SP; + case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_IMM_SP; + case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_IMM_SP; + case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_IMM_SP; + case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_IMM_SP; + case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_IMM_SP; + default: return MINT_NOP; + } +} + +static int +get_binop_condbr_sp (int opcode) +{ + switch (opcode) { + case MINT_BEQ_I4: return MINT_BEQ_I4_SP; + case MINT_BEQ_I8: return MINT_BEQ_I8_SP; + case MINT_BGE_I4: return MINT_BGE_I4_SP; + case MINT_BGE_I8: return MINT_BGE_I8_SP; + case MINT_BGT_I4: return MINT_BGT_I4_SP; + case MINT_BGT_I8: return MINT_BGT_I8_SP; + case MINT_BLT_I4: return MINT_BLT_I4_SP; + case MINT_BLT_I8: return MINT_BLT_I8_SP; + case MINT_BLE_I4: return MINT_BLE_I4_SP; + case MINT_BLE_I8: return MINT_BLE_I8_SP; + case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_SP; + case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_SP; + case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_SP; + case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_SP; + case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_SP; + case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_SP; + case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_SP; + case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_SP; + case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_SP; + case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_SP; + default: return MINT_NOP; + } +} + +static int +get_unop_condbr_sp (int opcode) +{ + switch (opcode) { + case MINT_BRFALSE_I4: return MINT_BRFALSE_I4_SP; + case MINT_BRFALSE_I8: return MINT_BRFALSE_I8_SP; + case MINT_BRTRUE_I4: return MINT_BRTRUE_I4_SP; + case MINT_BRTRUE_I8: return MINT_BRTRUE_I8_SP; + default: return MINT_NOP; + } +} + +static void +interp_super_instructions (TransformData *td) +{ + InterpBasicBlock *bb; + int *local_ref_count = td->local_ref_count; + + interp_compute_native_offset_estimates (td); + + // Add some actual super instructions + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + int noe; + + // Set cbb since we do some instruction inserting below + td->cbb = bb; + noe = bb->native_offset_estimate; + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + if (MINT_IS_NOP (opcode)) + continue; + if (mono_interp_op_dregs [opcode] && !td->vars [ins->dreg].global) + td->vars [ins->dreg].def = ins; + + if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { + // ldc + ret -> ret.imm + int sreg = ins->sregs [0]; + gint16 imm; + if (get_sreg_imm (td, sreg, &imm, (opcode == MINT_RET) ? MINT_TYPE_I2 : opcode - MINT_RET_I1)) { + InterpInst *def = td->vars [sreg].def; + int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); + new_inst->data [0] = imm; + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg]--; + + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8 || + opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8) { + int sreg = -1; + int sreg_imm = -1; + gint16 imm; + if (get_sreg_imm (td, ins->sregs [0], &imm, MINT_TYPE_I2)) { + sreg = ins->sregs [1]; + sreg_imm = ins->sregs [0]; + } else if (get_sreg_imm (td, ins->sregs [1], &imm, MINT_TYPE_I2)) { + sreg = ins->sregs [0]; + sreg_imm = ins->sregs [1]; + } + if (sreg != -1) { + int binop; + switch (opcode) { + case MINT_ADD_I4: binop = MINT_ADD_I4_IMM; break; + case MINT_ADD_I8: binop = MINT_ADD_I8_IMM; break; + case MINT_MUL_I4: binop = MINT_MUL_I4_IMM; break; + case MINT_MUL_I8: binop = MINT_MUL_I8_IMM; break; + default: g_assert_not_reached (); + } + InterpInst *new_inst = interp_insert_ins (td, ins, binop); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = sreg; + new_inst->data [0] = imm; + interp_clear_ins (td->vars [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } else if (opcode == MINT_SUB_I4 || opcode == MINT_SUB_I8) { + // ldc + sub -> add.-imm + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2) && imm != G_MININT16) { + int add_op = opcode == MINT_SUB_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, add_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = -imm; + interp_clear_ins (td->vars [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } else if (opcode == MINT_MUL_I4_IMM || opcode == MINT_MUL_I8_IMM) { + int sreg = ins->sregs [0]; + InterpInst *def = td->vars [sreg].def; + if (def != NULL && td->local_ref_count [sreg] == 1) { + gboolean is_i4 = opcode == MINT_MUL_I4_IMM; + if ((is_i4 && def->opcode == MINT_ADD_I4_IMM) || + (!is_i4 && def->opcode == MINT_ADD_I8_IMM)) { + InterpInst *new_inst = interp_insert_ins (td, ins, is_i4 ? MINT_ADD_MUL_I4_IMM : MINT_ADD_MUL_I8_IMM); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = def->sregs [0]; + new_inst->data [0] = def->data [0]; + new_inst->data [1] = ins->data [0]; + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_BINOP_SHIFT (opcode)) { + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { + // ldc + sh -> sh.imm + int shift_op = MINT_SHR_UN_I4_IMM + (opcode - MINT_SHR_UN_I4); + InterpInst *new_inst = interp_insert_ins (td, ins, shift_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = imm; + interp_clear_ins (td->vars [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } else if (opcode == MINT_SHL_I4 || opcode == MINT_SHL_I8) { + int amount_var = ins->sregs [1]; + InterpInst *amount_def = td->vars [amount_var].def; + if (amount_def != NULL && td->local_ref_count [amount_var] == 1 && amount_def->opcode == MINT_AND_I4) { + int mask_var = amount_def->sregs [1]; + if (get_sreg_imm (td, mask_var, &imm, MINT_TYPE_I2)) { + // ldc + and + shl -> shl_and_imm + int new_opcode = -1; + if (opcode == MINT_SHL_I4 && imm == 31) + new_opcode = MINT_SHL_AND_I4; + else if (opcode == MINT_SHL_I8 && imm == 63) + new_opcode = MINT_SHL_AND_I8; + + if (new_opcode != -1) { + InterpInst *new_inst = interp_insert_ins (td, ins, new_opcode); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->sregs [1] = amount_def->sregs [0]; + + local_ref_count [amount_var]--; + local_ref_count [mask_var]--; + + interp_clear_ins (td->vars [mask_var].def); + interp_clear_ins (amount_def); + interp_clear_ins (ins); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } + } + } else if (opcode == MINT_DIV_UN_I4 || opcode == MINT_DIV_UN_I8) { + // ldc + div.un -> shr.imm + int sreg_imm = ins->sregs [1]; + InterpInst *def = td->vars [sreg_imm].def; + if (def != NULL && td->local_ref_count [sreg_imm] == 1) { + int power2 = -1; + if (MINT_IS_LDC_I4 (def->opcode)) { + guint32 ct = interp_get_const_from_ldc_i4 (def); + power2 = mono_is_power_of_two ((guint32)ct); + } else if (MINT_IS_LDC_I8 (def->opcode)) { + guint64 ct = interp_get_const_from_ldc_i8 (def); + if (ct < G_MAXUINT32) + power2 = mono_is_power_of_two ((guint32)ct); + } + if (power2 > 0) { + InterpInst *new_inst; + if (opcode == MINT_DIV_UN_I4) + new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I4_IMM); + else + new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I8_IMM); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = GINT_TO_UINT16 (power2); + + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("lower div.un: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_LDIND_INT (opcode)) { + int sreg_base = ins->sregs [0]; + InterpInst *def = td->vars [sreg_base].def; + if (def != NULL && td->local_ref_count [sreg_base] == 1) { + InterpInst *new_inst = NULL; + if (def->opcode == MINT_ADD_P) { + int ldind_offset_op = MINT_LDIND_OFFSET_I1 + (opcode - MINT_LDIND_I1); + new_inst = interp_insert_ins (td, ins, ldind_offset_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->sregs [1] = def->sregs [1]; // off + } else if (def->opcode == MINT_ADD_P_IMM) { + int ldind_offset_imm_op = MINT_LDIND_OFFSET_IMM_I1 + (opcode - MINT_LDIND_I1); + new_inst = interp_insert_ins (td, ins, ldind_offset_imm_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->data [0] = def->data [0]; // imm value + } + if (new_inst) { + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_base]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_LDIND_OFFSET (opcode)) { + int sreg_off = ins->sregs [1]; + InterpInst *def = td->vars [sreg_off].def; + if (def != NULL && td->local_ref_count [sreg_off] == 1) { + if (def->opcode == MINT_MUL_P_IMM || def->opcode == MINT_ADD_P_IMM || def->opcode == MINT_ADD_MUL_P_IMM) { + int ldind_offset_op = MINT_LDIND_OFFSET_ADD_MUL_IMM_I1 + (opcode - MINT_LDIND_OFFSET_I1); + InterpInst *new_inst = interp_insert_ins (td, ins, ldind_offset_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; // base + new_inst->sregs [1] = def->sregs [0]; // off + + // set the add and mul immediates + switch (def->opcode) { + case MINT_ADD_P_IMM: + new_inst->data [0] = def->data [0]; + new_inst->data [1] = 1; + break; + case MINT_MUL_P_IMM: + new_inst->data [0] = 0; + new_inst->data [1] = def->data [0]; + break; + case MINT_ADD_MUL_P_IMM: + new_inst->data [0] = def->data [0]; + new_inst->data [1] = def->data [1]; + break; + } + + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_off]--; + if (td->verbose_level) { + g_print ("method %s:%s, superins: ", m_class_get_name (td->method->klass), td->method->name); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_STIND_INT (opcode)) { + int sreg_base = ins->sregs [0]; + InterpInst *def = td->vars [sreg_base].def; + if (def != NULL && td->local_ref_count [sreg_base] == 1) { + InterpInst *new_inst = NULL; + if (def->opcode == MINT_ADD_P) { + int stind_offset_op = MINT_STIND_OFFSET_I1 + (opcode - MINT_STIND_I1); + new_inst = interp_insert_ins (td, ins, stind_offset_op); + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->sregs [1] = def->sregs [1]; // off + new_inst->sregs [2] = ins->sregs [1]; // value + } else if (def->opcode == MINT_ADD_P_IMM) { + int stind_offset_imm_op = MINT_STIND_OFFSET_IMM_I1 + (opcode - MINT_STIND_I1); + new_inst = interp_insert_ins (td, ins, stind_offset_imm_op); + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->sregs [1] = ins->sregs [1]; // value + new_inst->data [0] = def->data [0]; // imm value + } + if (new_inst) { + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_base]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_LDFLD (opcode)) { + // cknull + ldfld -> ldfld + // FIXME This optimization is very limited, it is meant mainly to remove cknull + // when inlining property accessors. We should have more advanced cknull removal + // optimzations, so we can catch cases where instructions are not next to each other. + int obj_sreg = ins->sregs [0]; + InterpInst *def = td->vars [obj_sreg].def; + if (def != NULL && def->opcode == MINT_CKNULL && interp_prev_ins (ins) == def && + def->dreg == obj_sreg && local_ref_count [obj_sreg] == 1) { + if (td->verbose_level) { + g_print ("remove redundant cknull (%s): ", td->method->name); + interp_dump_ins (def, td->data_items); + } + ins->sregs [0] = def->sregs [0]; + interp_clear_ins (def); + local_ref_count [obj_sreg]--; + } + } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && interp_is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { + int condbr_op = get_binop_condbr_imm_sp (opcode); + if (condbr_op != MINT_NOP) { + InterpInst *prev_ins = interp_prev_ins (ins); + // The new instruction does a safepoint + if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) + interp_clear_ins (prev_ins); + InterpInst *new_ins = interp_insert_ins (td, ins, condbr_op); + new_ins->sregs [0] = ins->sregs [0]; + new_ins->data [0] = imm; + new_ins->info.target_bb = ins->info.target_bb; + interp_clear_ins (td->vars [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_ins, td->data_items); + } + } + } else { + InterpInst *prev_ins = interp_prev_ins (ins); + if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { + int condbr_op = get_binop_condbr_sp (opcode); + if (condbr_op != MINT_NOP) { + interp_clear_ins (prev_ins); + ins->opcode = GINT_TO_OPCODE (condbr_op); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (ins, td->data_items); + } + } + } + } + } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode) && interp_is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { + if (opcode == MINT_BRFALSE_I4 || opcode == MINT_BRTRUE_I4) { + gboolean negate = opcode == MINT_BRFALSE_I4; + int cond_sreg = ins->sregs [0]; + InterpInst *def = td->vars [cond_sreg].def; + if (def != NULL && local_ref_count [cond_sreg] == 1) { + int replace_opcode = -1; + switch (def->opcode) { + case MINT_CEQ_I4: replace_opcode = negate ? MINT_BNE_UN_I4 : MINT_BEQ_I4; break; + case MINT_CEQ_I8: replace_opcode = negate ? MINT_BNE_UN_I8 : MINT_BEQ_I8; break; + case MINT_CGT_I4: replace_opcode = negate ? MINT_BLE_I4 : MINT_BGT_I4; break; + case MINT_CGT_I8: replace_opcode = negate ? MINT_BLE_I8 : MINT_BGT_I8; break; + case MINT_CLT_I4: replace_opcode = negate ? MINT_BGE_I4 : MINT_BLT_I4; break; + case MINT_CLT_I8: replace_opcode = negate ? MINT_BGE_I8 : MINT_BLT_I8; break; + case MINT_CGT_UN_I4: replace_opcode = negate ? MINT_BLE_UN_I4 : MINT_BGT_UN_I4; break; + case MINT_CGT_UN_I8: replace_opcode = negate ? MINT_BLE_UN_I8 : MINT_BGT_UN_I8; break; + case MINT_CLT_UN_I4: replace_opcode = negate ? MINT_BGE_UN_I4 : MINT_BLT_UN_I4; break; + case MINT_CLT_UN_I8: replace_opcode = negate ? MINT_BGE_UN_I8 : MINT_BLT_UN_I8; break; + case MINT_CEQ_R4: replace_opcode = negate ? MINT_BNE_UN_R4 : MINT_BEQ_R4; break; + case MINT_CEQ_R8: replace_opcode = negate ? MINT_BNE_UN_R8 : MINT_BEQ_R8; break; + case MINT_CGT_R4: replace_opcode = negate ? MINT_BLE_UN_R4 : MINT_BGT_R4; break; + case MINT_CGT_R8: replace_opcode = negate ? MINT_BLE_UN_R8 : MINT_BGT_R8; break; + case MINT_CLT_R4: replace_opcode = negate ? MINT_BGE_UN_R4 : MINT_BLT_R4; break; + case MINT_CLT_R8: replace_opcode = negate ? MINT_BGE_UN_R8 : MINT_BLT_R8; break; + case MINT_CGT_UN_R4: replace_opcode = negate ? MINT_BLE_R4 : MINT_BGT_UN_R4; break; + case MINT_CGT_UN_R8: replace_opcode = negate ? MINT_BLE_R8 : MINT_BGT_UN_R8; break; + case MINT_CLT_UN_R4: replace_opcode = negate ? MINT_BGE_R4 : MINT_BLT_UN_R4; break; + case MINT_CLT_UN_R8: replace_opcode = negate ? MINT_BGE_R8 : MINT_BLT_UN_R8; break; + case MINT_CEQ0_I4: replace_opcode = negate ? MINT_BRTRUE_I4 : MINT_BRFALSE_I4; break; // If def->opcode is MINT_CEQ0_I4 ins->opcode is inverted + // Add more opcodes + default: + break; + } + if (replace_opcode != -1) { + ins->opcode = GINT_TO_UINT16 (replace_opcode); + ins->sregs [0] = def->sregs [0]; + if (def->opcode != MINT_CEQ0_I4) + ins->sregs [1] = def->sregs [1]; + interp_clear_ins (def); + local_ref_count [cond_sreg]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (ins, td->data_items); + } + // The newly added opcode could be part of further superinstructions. Retry + ins = ins->prev; + continue; + } + } + } + InterpInst *prev_ins = interp_prev_ins (ins); + if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { + int condbr_op = get_unop_condbr_sp (opcode); + if (condbr_op != MINT_NOP) { + interp_clear_ins (prev_ins); + ins->opcode = GINT_TO_OPCODE (condbr_op); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (ins, td->data_items); + } + } + } + } else if (opcode == MINT_STOBJ_VT_NOREF) { + int sreg_src = ins->sregs [1]; + InterpInst *def = td->vars [sreg_src].def; + if (def != NULL && interp_prev_ins (ins) == def && def->opcode == MINT_LDOBJ_VT && ins->data [0] == def->data [0] && td->local_ref_count [sreg_src] == 1) { + InterpInst *new_inst = interp_insert_ins (td, ins, MINT_CPOBJ_VT_NOREF); + new_inst->sregs [0] = ins->sregs [0]; // dst + new_inst->sregs [1] = def->sregs [0]; // src + new_inst->data [0] = ins->data [0]; // size + + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_src]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + noe += interp_get_ins_length (ins); + } + } +} + +void +interp_optimize_code (TransformData *td) +{ + if (mono_interp_opt & INTERP_OPT_BBLOCKS) + interp_optimize_bblocks (td); + + if (mono_interp_opt & INTERP_OPT_CPROP) + MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); + + // After this point control optimizations on control flow can no longer happen, so we can determine + // which vars are global. This helps speed up the super instructions pass, which only operates on + // single def, single use local vars. + initialize_global_vars (td); + + if ((mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) && + (mono_interp_opt & INTERP_OPT_CPROP)) + MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); +} + diff --git a/src/mono/mono/mini/interp/transform-simd.c b/src/mono/mono/mini/interp/transform-simd.c index 45fdb8ed592046..ee0501db4658e2 100644 --- a/src/mono/mono/mini/interp/transform-simd.c +++ b/src/mono/mono/mini/interp/transform-simd.c @@ -315,7 +315,7 @@ emit_common_simd_epilogue (TransformData *td, MonoClass *vector_klass, MonoMetho { td->sp -= csignature->param_count; for (int i = 0; i < csignature->param_count; i++) - td->last_ins->sregs [i] = td->sp [i].local; + td->last_ins->sregs [i] = td->sp [i].var; int ret_mt = mono_mint_type (csignature->ret); if (csignature->ret->type == MONO_TYPE_VOID) { @@ -324,10 +324,10 @@ emit_common_simd_epilogue (TransformData *td, MonoClass *vector_klass, MonoMetho } else if (ret_mt == MINT_TYPE_VT) { // For these intrinsics, if we return a VT then it is a V128 push_type_vt (td, vector_klass, vector_size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { push_simple_type (td, stack_type [ret_mt]); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; } @@ -347,14 +347,14 @@ emit_vector_create (TransformData *td, MonoMethodSignature *csignature, MonoClas int *call_args = (int*)mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); td->sp -= csignature->param_count; for (int i = 0; i < num_args; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [num_args] = -1; init_last_ins_call (td); td->last_ins->info.call_info->call_args = call_args; if (!td->optimized) td->last_ins->info.call_info->call_offset = get_tos_offset (td); push_type_vt (td, vector_klass, vector_size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static gboolean diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 464ddebd676132..52dcf65ec48284 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -94,7 +94,7 @@ has_doesnotreturn_attribute (MonoMethod *method) return result; } -static InterpInst* +InterpInst* interp_new_ins (TransformData *td, int opcode, int len) { InterpInst *new_inst; @@ -127,7 +127,7 @@ interp_add_ins (TransformData *td, int opcode) return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]); } -static InterpInst* +InterpInst* interp_insert_ins_bb (TransformData *td, InterpBasicBlock *bb, InterpInst *prev_ins, int opcode) { InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]); @@ -152,13 +152,13 @@ interp_insert_ins_bb (TransformData *td, InterpBasicBlock *bb, InterpInst *prev_ } /* Inserts a new instruction after prev_ins. prev_ins must be in cbb */ -static InterpInst* +InterpInst* interp_insert_ins (TransformData *td, InterpInst *prev_ins, int opcode) { return interp_insert_ins_bb (td, td->cbb, prev_ins, opcode); } -static void +void interp_clear_ins (InterpInst *ins) { // Clearing instead of removing from the list makes everything easier. @@ -168,13 +168,13 @@ interp_clear_ins (InterpInst *ins) ins->opcode = MINT_NOP; } -static gboolean +gboolean interp_ins_is_nop (InterpInst *ins) { return ins->opcode == MINT_NOP || ins->opcode == MINT_IL_SEQ_POINT; } -static InterpInst* +InterpInst* interp_prev_ins (InterpInst *ins) { ins = ins->prev; @@ -183,7 +183,7 @@ interp_prev_ins (InterpInst *ins) return ins; } -static InterpInst* +InterpInst* interp_next_ins (InterpInst *ins) { ins = ins->next; @@ -203,26 +203,6 @@ check_stack_helper (TransformData *td, int n) return TRUE; } -static InterpInst* -interp_first_ins (InterpBasicBlock *bb) -{ - InterpInst *ins = bb->first_ins; - if (!ins || !interp_ins_is_nop (ins)) - return ins; - while (ins && interp_ins_is_nop (ins)) - ins = ins->next; - return ins; -} - -static InterpInst* -interp_last_ins (InterpBasicBlock *bb) -{ - InterpInst *ins = bb->last_ins; - if (!ins || !interp_ins_is_nop (ins)) - return ins; - return interp_prev_ins (ins); -} - #define CHECK_STACK(td, n) \ do { \ if (!check_stack_helper (td, n)) \ @@ -287,7 +267,7 @@ get_stack_size (TransformData *td, StackInfo *sp, int count) int result = 0; for (int i = 0; i < count; i++) { result += sp [i].size; - if (td->locals [sp [i].local].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [sp [i].var].simd) result = ALIGN_TO (result, MINT_SIMD_ALIGNMENT); } return result; @@ -376,22 +356,22 @@ mono_mint_type (MonoType *type) * way, with an offset relative to the frame->locals. */ static int -create_interp_local_explicit (TransformData *td, MonoType *type, int size) +interp_create_var_explicit (TransformData *td, MonoType *type, int size) { - if (td->locals_size == td->locals_capacity) { - td->locals_capacity *= 2; - if (td->locals_capacity == 0) - td->locals_capacity = 2; - td->locals = (InterpLocal*) g_realloc (td->locals, td->locals_capacity * sizeof (InterpLocal)); + if (td->vars_size == td->vars_capacity) { + td->vars_capacity *= 2; + if (td->vars_capacity == 0) + td->vars_capacity = 2; + td->vars = (InterpVar*) g_realloc (td->vars, td->vars_capacity * sizeof (InterpVar)); } int mt = mono_mint_type (type); - InterpLocal *local = &td->locals [td->locals_size]; + InterpVar *local = &td->vars [td->vars_size]; + memset (local, 0, sizeof (InterpVar)); local->type = type; local->mt = mt; - local->flags = 0; if (mt == MINT_TYPE_VT && m_class_is_simd_type (mono_class_from_mono_type_internal (type))) - local->flags |= INTERP_LOCAL_FLAG_SIMD; + local->simd = TRUE; local->indirects = 0; local->offset = -1; local->size = size; @@ -399,18 +379,18 @@ create_interp_local_explicit (TransformData *td, MonoType *type, int size) local->bb_index = -1; local->def = NULL; - td->locals_size++; - return td->locals_size - 1; + td->vars_size++; + return td->vars_size - 1; } static void -create_interp_dummy_var (TransformData *td) +interp_create_dummy_var (TransformData *td) { g_assert (td->dummy_var < 0); - td->dummy_var = create_interp_local_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); - td->locals [td->dummy_var].offset = 0; - td->locals [td->dummy_var].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->dummy_var = interp_create_var_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); + td->vars [td->dummy_var].offset = 0; + td->vars [td->dummy_var].global = TRUE; } static int @@ -424,12 +404,12 @@ get_tos_offset (TransformData *td) // Create a local for sp static void -create_interp_stack_local (TransformData *td, StackInfo *sp, int type_size) +interp_create_stack_var (TransformData *td, StackInfo *sp, int type_size) { - int local = create_interp_local_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); + int local = interp_create_var_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); - td->locals [local].flags |= INTERP_LOCAL_FLAG_EXECUTION_STACK; - sp->local = local; + td->vars [local].execution_stack = TRUE; + sp->var = local; } static void @@ -452,12 +432,12 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) sp->klass = k; sp->flags = 0; sp->size = ALIGN_TO (type_size, MINT_STACK_SLOT_SIZE); - create_interp_stack_local (td, sp, type_size); + interp_create_stack_var (td, sp, type_size); if (!td->optimized) { sp->offset = get_tos_offset (td); - if (td->locals [sp->local].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [sp->var].simd) sp->offset = ALIGN_TO (sp->offset, MINT_SIMD_ALIGNMENT); - td->locals [sp->local].stack_offset = sp->offset; + td->vars [sp->var].stack_offset = sp->offset; // Additional space that is allocated for the frame, when we don't run the var offset allocator ENSURE_STACK_SIZE(td, sp->offset + sp->size); } @@ -467,13 +447,13 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) static void push_var (TransformData *td, int var_index) { - InterpLocal *var = &td->locals [var_index]; + InterpVar *var = &td->vars [var_index]; ensure_stack (td, 1); StackInfo *sp = td->sp; sp->type = GINT_TO_UINT8 (stack_type [var->mt]); sp->klass = mono_class_from_mono_type_internal (var->type); sp->flags = 0; - sp->local = var_index; + sp->var = var_index; sp->size = ALIGN_TO (var->size, MINT_STACK_SLOT_SIZE); td->sp++; } @@ -499,18 +479,18 @@ push_var (TransformData *td, int var_index) } while (0) static void -set_type_and_local (TransformData *td, StackInfo *sp, int type, MonoClass *klass) +set_type_and_var (TransformData *td, StackInfo *sp, int type, MonoClass *klass) { SET_TYPE (sp, type, klass); - create_interp_stack_local (td, sp, MINT_STACK_SLOT_SIZE); + interp_create_stack_var (td, sp, MINT_STACK_SLOT_SIZE); if (!td->optimized) - td->locals [sp->local].stack_offset = sp->offset; + td->vars [sp->var].stack_offset = sp->offset; } static void -set_simple_type_and_local (TransformData *td, StackInfo *sp, int type) +set_simple_type_and_var (TransformData *td, StackInfo *sp, int type) { - set_type_and_local (td, sp, type, NULL); + set_type_and_var (td, sp, type, NULL); } static void @@ -539,168 +519,8 @@ push_types (TransformData *td, StackInfo *types, int count) push_type_explicit (td, types [i].type, types [i].klass, types [i].size); } -static void -mark_bb_as_dead (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *replace_bb) -{ - // Update IL offset to bb mapping so that offset_to_bb doesn't point to dead - // bblocks. This mapping can still be needed when computing clause ranges. Since - // multiple IL offsets can end up pointing to same bblock after optimizations, - // make sure we update mapping for all of them - // - // To avoid scanning the entire offset_to_bb array, we scan only in the vicinity - // of the IL offset of bb. We can stop search when we encounter a different bblock. - g_assert (bb->il_offset >= 0); - for (int il_offset = bb->il_offset; il_offset >= 0; il_offset--) { - if (td->offset_to_bb [il_offset] == bb) - td->offset_to_bb [il_offset] = replace_bb; - else if (td->offset_to_bb [il_offset]) - break; - } - for (guint32 il_offset = bb->il_offset + 1; il_offset < td->header->code_size; il_offset++) { - if (td->offset_to_bb [il_offset] == bb) - td->offset_to_bb [il_offset] = replace_bb; - else if (td->offset_to_bb [il_offset]) - break; - } - - bb->dead = TRUE; - // bb should never be used/referenced after this -} - -/* Merges two consecutive bbs (in code order) into a single one */ -static void -interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *bbadd) -{ - g_assert (bbadd->in_count == 1 && bbadd->in_bb [0] == bb); - g_assert (bb->next_bb == bbadd); - - // Remove the branch instruction to the invalid bblock - if (bb->last_ins) { - InterpInst *last_ins = (bb->last_ins->opcode != MINT_NOP) ? bb->last_ins : interp_prev_ins (bb->last_ins); - if (last_ins) { - if (last_ins->opcode == MINT_BR) { - g_assert (last_ins->info.target_bb == bbadd); - interp_clear_ins (last_ins); - } else if (last_ins->opcode == MINT_SWITCH) { - // Weird corner case where empty switch can branch by default to next instruction - last_ins->opcode = MINT_NOP; - } - } - } - - // Append all instructions from bbadd to bb - if (bb->last_ins) { - if (bbadd->first_ins) { - bb->last_ins->next = bbadd->first_ins; - bbadd->first_ins->prev = bb->last_ins; - bb->last_ins = bbadd->last_ins; - } - } else { - bb->first_ins = bbadd->first_ins; - bb->last_ins = bbadd->last_ins; - } - bb->next_bb = bbadd->next_bb; - - // Fixup bb links - bb->out_count = bbadd->out_count; - bb->out_bb = bbadd->out_bb; - for (int i = 0; i < bbadd->out_count; i++) { - for (int j = 0; j < bbadd->out_bb [i]->in_count; j++) { - if (bbadd->out_bb [i]->in_bb [j] == bbadd) - bbadd->out_bb [i]->in_bb [j] = bb; - } - } - - mark_bb_as_dead (td, bbadd, bb); -} - -// array must contain ref -static void -remove_bblock_ref (InterpBasicBlock **array, InterpBasicBlock *ref, int len) -{ - int i = 0; - while (array [i] != ref) - i++; - i++; - while (i < len) { - array [i - 1] = array [i]; - i++; - } -} - -static void -interp_unlink_bblocks (InterpBasicBlock *from, InterpBasicBlock *to) -{ - remove_bblock_ref (from->out_bb, to, from->out_count); - from->out_count--; - remove_bblock_ref (to->in_bb, from, to->in_count); - to->in_count--; -} - -static gboolean -interp_remove_bblock (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *prev_bb) -{ - gboolean needs_cprop = FALSE; - - for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { - if (ins->opcode == MINT_LDLOCA_S) { - td->locals [ins->sregs [0]].indirects--; - if (!td->locals [ins->sregs [0]].indirects) { - // We can do cprop now through this local. Run cprop again. - needs_cprop = TRUE; - } - } - } - while (bb->in_count) - interp_unlink_bblocks (bb->in_bb [0], bb); - while (bb->out_count) - interp_unlink_bblocks (bb, bb->out_bb [0]); - prev_bb->next_bb = bb->next_bb; - mark_bb_as_dead (td, bb, bb->next_bb); - - return needs_cprop; -} - -static void -interp_link_bblocks (TransformData *td, InterpBasicBlock *from, InterpBasicBlock *to) -{ - int i; - gboolean found = FALSE; - - for (i = 0; i < from->out_count; ++i) { - if (to == from->out_bb [i]) { - found = TRUE; - break; - } - } - if (!found) { - InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (from->out_count + 1)); - for (i = 0; i < from->out_count; ++i) - newa [i] = from->out_bb [i]; - newa [i] = to; - from->out_count++; - from->out_bb = newa; - } - - found = FALSE; - for (i = 0; i < to->in_count; ++i) { - if (from == to->in_bb [i]) { - found = TRUE; - break; - } - } - if (!found) { - InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (to->in_count + 1)); - for (i = 0; i < to->in_count; ++i) - newa [i] = to->in_bb [i]; - newa [i] = from; - to->in_count++; - to->in_bb = newa; - } -} - -static int -get_mov_for_type (int mt, gboolean needs_sext) +int +interp_get_mov_for_type (int mt, gboolean needs_sext) { switch (mt) { case MINT_TYPE_I1: @@ -768,22 +588,22 @@ fixup_newbb_stack_locals (TransformData *td, InterpBasicBlock *newbb) return; for (int i = 0; i < newbb->stack_height; i++) { - int sloc = td->stack [i].local; - int dloc = newbb->stack_state [i].local; + int sloc = td->stack [i].var; + int dloc = newbb->stack_state [i].var; if (sloc != dloc) { - int mt = td->locals [sloc].mt; - int mov_op = get_mov_for_type (mt, FALSE); + int mt = td->vars [sloc].mt; + int mov_op = interp_get_mov_for_type (mt, FALSE); // FIXME can be hit in some IL cases. Should we merge the stack states ? (b41002.il) - // g_assert (mov_op == get_mov_for_type (td->locals [dloc].mt, FALSE)); + // g_assert (mov_op == interp_get_mov_for_type (td->vars [dloc].mt, FALSE)); interp_add_ins (td, mov_op); - interp_ins_set_sreg (td->last_ins, td->stack [i].local); - interp_ins_set_dreg (td->last_ins, newbb->stack_state [i].local); + interp_ins_set_sreg (td->last_ins, td->stack [i].var); + interp_ins_set_dreg (td->last_ins, newbb->stack_state [i].var); if (mt == MINT_TYPE_VT) { - g_assert (td->locals [sloc].size == td->locals [dloc].size); - td->last_ins->data [0] = GINT_TO_UINT16 (td->locals [sloc].size); + g_assert (td->vars [sloc].size == td->vars [dloc].size); + td->last_ins->data [0] = GINT_TO_UINT16 (td->vars [sloc].size); } } } @@ -868,7 +688,7 @@ one_arg_branch(TransformData *td, int mint_op, int offset, int inst_size) --td->sp; if (offset) { handle_branch (td, long_op, offset + inst_size); - interp_ins_set_sreg (td->last_ins, td->sp->local); + interp_ins_set_sreg (td->last_ins, td->sp->var); } else { interp_add_ins (td, MINT_NOP); } @@ -883,9 +703,9 @@ interp_add_conv (TransformData *td, StackInfo *sp, InterpInst *prev_ins, int typ else new_inst = interp_add_ins (td, conv_op); - interp_ins_set_sreg (new_inst, sp->local); - set_simple_type_and_local (td, sp, type); - interp_ins_set_dreg (new_inst, sp->local); + interp_ins_set_sreg (new_inst, sp->var); + set_simple_type_and_var (td, sp, type); + interp_ins_set_dreg (new_inst, sp->var); } static void @@ -916,7 +736,7 @@ two_arg_branch(TransformData *td, int mint_op, int offset, int inst_size) td->sp -= 2; if (offset) { handle_branch (td, long_op, offset + inst_size); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } else { interp_add_ins (td, MINT_NOP); } @@ -929,9 +749,9 @@ unary_arith_op(TransformData *td, int mint_op) int op = mint_op + td->sp [-1].type - STACK_TYPE_I4; td->sp--; interp_add_ins (td, op); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, td->sp [0].type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static void @@ -971,9 +791,9 @@ binary_arith_op(TransformData *td, int mint_op) op = mint_op + type1 - STACK_TYPE_I4; td->sp -= 2; interp_add_ins (td, op); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, type1); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static void @@ -988,9 +808,9 @@ shift_op(TransformData *td, int mint_op) } td->sp -= 2; interp_add_ins (td, op); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, td->sp [0].type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static int @@ -1057,9 +877,9 @@ load_arg(TransformData *td, int n) } push_type (td, stack_type [mt], klass); } - interp_add_ins (td, get_mov_for_type (mt, TRUE)); + interp_add_ins (td, interp_get_mov_for_type (mt, TRUE)); interp_ins_set_sreg (td->last_ins, n); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); } @@ -1083,8 +903,8 @@ store_arg(TransformData *td, int n) g_assert (size < G_MAXUINT16); } --td->sp; - interp_add_ins (td, get_mov_for_type (mt, FALSE)); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); interp_ins_set_dreg (td->last_ins, n); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); @@ -1093,9 +913,9 @@ store_arg(TransformData *td, int n) static void load_local (TransformData *td, int local) { - int mt = td->locals [local].mt; - gint32 size = td->locals [local].size; - MonoType *type = td->locals [local].type; + int mt = td->vars [local].mt; + gint32 size = td->vars [local].size; + MonoType *type = td->vars [local].type; if (mt == MINT_TYPE_VT) { MonoClass *klass = mono_class_from_mono_type_internal (type); @@ -1106,9 +926,9 @@ load_local (TransformData *td, int local) klass = mono_class_from_mono_type_internal (type); push_type (td, stack_type [mt], klass); } - interp_add_ins (td, get_mov_for_type (mt, TRUE)); + interp_add_ins (td, interp_get_mov_for_type (mt, TRUE)); interp_ins_set_sreg (td->last_ins, local); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); } @@ -1116,7 +936,7 @@ load_local (TransformData *td, int local) static void store_local (TransformData *td, int local) { - int mt = td->locals [local].mt; + int mt = td->vars [local].mt; CHECK_STACK_RET_VOID (td, 1); #if SIZEOF_VOID_P == 8 // nint and int32 can be used interchangeably. Add implicit conversions. @@ -1136,11 +956,11 @@ store_local (TransformData *td, int local) stack_type [mt], td->sp [-1].type); } --td->sp; - interp_add_ins (td, get_mov_for_type (mt, FALSE)); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); interp_ins_set_dreg (td->last_ins, local); if (mt == MINT_TYPE_VT) - td->last_ins->data [0] = GINT32_TO_UINT16 (td->locals [local].size); + td->last_ins->data [0] = GINT32_TO_UINT16 (td->vars [local].size); } static void @@ -1269,7 +1089,7 @@ emit_ldptr (TransformData *td, gpointer data) { interp_add_ins (td, MINT_LDPTR); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, data); } @@ -1306,7 +1126,7 @@ interp_generate_icall_throw (TransformData *td, MonoJitICallInfo *icall_info, gp if (num_args) { int *call_args = (int*)mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); for (int i = 0; i < num_args; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [num_args] = -1; td->last_ins->info.call_info->call_args = call_args; } @@ -1349,40 +1169,15 @@ interp_generate_ipe_bad_fallthru (TransformData *td) } -static int -create_interp_local (TransformData *td, MonoType *type) +int +interp_create_var (TransformData *td, MonoType *type) { int size, align; size = mono_type_size (type, &align); g_assert (align <= MINT_STACK_SLOT_SIZE); - return create_interp_local_explicit (td, type, size); -} - -// Allocates var at the offset that tos points to, also updating it. -static int -alloc_var_offset (TransformData *td, int local, gint32 *ptos) -{ - int size, offset; - - offset = *ptos; - size = td->locals [local].size; - - if (td->locals [local].flags & INTERP_LOCAL_FLAG_SIMD) - offset = ALIGN_TO (offset, MINT_SIMD_ALIGNMENT); - - td->locals [local].offset = offset; - - *ptos = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); - - return td->locals [local].offset; -} - -static int -alloc_global_var_offset (TransformData *td, int var) -{ - return alloc_var_offset (td, var, &td->total_locals_size); + return interp_create_var_explicit (td, type, size); } /* @@ -1392,7 +1187,7 @@ alloc_global_var_offset (TransformData *td, int var) * ip is the address where the arguments of the instruction are located */ static char* -dump_interp_ins_data (InterpInst *ins, gint32 ins_offset, const guint16 *data, int opcode, gpointer *data_items) +interp_dump_ins_data (InterpInst *ins, gint32 ins_offset, const guint16 *data, int opcode, gpointer *data_items) { GString *str = g_string_new (""); int target; @@ -1509,7 +1304,7 @@ dump_interp_ins_data (InterpInst *ins, gint32 ins_offset, const guint16 *data, i } static void -dump_interp_compacted_ins (const guint16 *ip, const guint16 *start, gpointer *data_items) +interp_dump_compacted_ins (const guint16 *ip, const guint16 *start, gpointer *data_items) { int opcode = *ip; int ins_offset = GPTRDIFF_TO_INT (ip - start); @@ -1530,28 +1325,31 @@ dump_interp_compacted_ins (const guint16 *ip, const guint16 *start, gpointer *da } else { g_string_append_printf (str, " nil],"); } - char *ins_data = dump_interp_ins_data (NULL, ins_offset, ip, opcode, data_items); + char *ins_data = interp_dump_ins_data (NULL, ins_offset, ip, opcode, data_items); g_print ("%s%s\n", str->str, ins_data); g_string_free (str, TRUE); g_free (ins_data); } static void -dump_interp_code (const guint16 *start, const guint16* end, gpointer *data_items) +interp_dump_code (const guint16 *start, const guint16* end, gpointer *data_items) { const guint16 *p = start; while (p < end) { - dump_interp_compacted_ins (p, start, data_items); + interp_dump_compacted_ins (p, start, data_items); p = mono_interp_dis_mintop_len (p); } } -static void -dump_interp_inst (InterpInst *ins, gpointer *data_items) +void +interp_dump_ins (InterpInst *ins, gpointer *data_items) { int opcode = ins->opcode; GString *str = g_string_new (""); - g_string_append_printf (str, "IL_%04x: %-14s", ins->il_offset, mono_interp_opname (opcode)); + if (ins->il_offset == -1) + g_string_append_printf (str, "IL_----: %-14s", mono_interp_opname (opcode)); + else + g_string_append_printf (str, "IL_%04x: %-14s", ins->il_offset, mono_interp_opname (opcode)); if (mono_interp_op_dregs [opcode] > 0) g_string_append_printf (str, " [%d <-", ins->dreg); @@ -1582,7 +1380,7 @@ dump_interp_inst (InterpInst *ins, gpointer *data_items) // LDLOCA has special semantics, it has data in sregs [0], but it doesn't have any sregs g_string_append_printf (str, " %d", ins->sregs [0]); } else { - char *descr = dump_interp_ins_data (ins, ins->il_offset, &ins->data [0], ins->opcode, data_items); + char *descr = interp_dump_ins_data (ins, ins->il_offset, &ins->data [0], ins->opcode, data_items); g_string_append_printf (str, "%s", descr); g_free (descr); } @@ -1590,40 +1388,14 @@ dump_interp_inst (InterpInst *ins, gpointer *data_items) g_string_free (str, TRUE); } -static GString* -get_interp_bb_links (InterpBasicBlock *bb) -{ - GString *str = g_string_new (""); - - if (bb->in_count) { - g_string_append_printf (str, "IN (%d", bb->in_bb [0]->index); - for (int i = 1; i < bb->in_count; i++) - g_string_append_printf (str, " %d", bb->in_bb [i]->index); - g_string_append_printf (str, "), "); - } else { - g_string_append_printf (str, "IN (nil), "); - } - - if (bb->out_count) { - g_string_append_printf (str, "OUT (%d", bb->out_bb [0]->index); - for (int i = 1; i < bb->out_count; i++) - g_string_append_printf (str, " %d", bb->out_bb [i]->index); - g_string_append_printf (str, ")"); - } else { - g_string_append_printf (str, "OUT (nil)"); - } - - return str; -} - static void -dump_interp_bb (InterpBasicBlock *bb, gpointer *data_items) +interp_dump_bb (InterpBasicBlock *bb, gpointer *data_items) { g_print ("BB%d:\n", bb->index); for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { // Avoid some noise if (ins->opcode != MINT_NOP && ins->opcode != MINT_IL_SEQ_POINT) - dump_interp_inst (ins, data_items); + interp_dump_ins (ins, data_items); } } @@ -1643,7 +1415,7 @@ mono_interp_print_code (InterpMethod *imethod) g_free (name); start = (guint8*) jinfo->code_start; - dump_interp_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size), imethod->data_items); + interp_dump_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size), imethod->data_items); } /* For debug use */ @@ -1652,7 +1424,7 @@ mono_interp_print_td_code (TransformData *td) { g_print ("Unoptimized IR:\n"); for (InterpBasicBlock *bb = td->entry_bb; bb != NULL; bb = bb->next_bb) - dump_interp_bb (bb, td->data_items); + interp_dump_bb (bb, td->data_items); } @@ -1683,7 +1455,7 @@ interp_ins_is_ldc (InterpInst *ins) return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8; } -static gint32 +gint32 interp_get_const_from_ldc_i4 (InterpInst *ins) { switch (ins->opcode) { @@ -1704,20 +1476,8 @@ interp_get_const_from_ldc_i4 (InterpInst *ins) } } -static gint64 -interp_get_const_from_ldc_i8 (InterpInst *ins) -{ - switch (ins->opcode) { - case MINT_LDC_I8_0: return 0; - case MINT_LDC_I8_S: return (gint64)(gint16)ins->data [0]; - case MINT_LDC_I8: return READ64 (&ins->data [0]); - default: - g_assert_not_reached (); - } -} - /* If ins is not null, it will replace it with the ldc */ -static InterpInst* +InterpInst* interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int dreg) { guint16 opcode; @@ -1763,24 +1523,6 @@ interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int return ins; } -static InterpInst* -interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct) -{ - int size = mono_interp_oplen [ins->opcode]; - int dreg = ins->dreg; - - if (size < 5) { - ins = interp_insert_ins (td, ins, MINT_LDC_I8); - interp_clear_ins (ins->prev); - } else { - ins->opcode = MINT_LDC_I8; - } - WRITE64_INS (ins, 0, &ct); - ins->dreg = dreg; - - return ins; -} - static int interp_get_ldind_for_mt (int mt) { @@ -1800,24 +1542,6 @@ interp_get_ldind_for_mt (int mt) return -1; } -static int -interp_get_mt_for_ldind (int ldind_op) -{ - switch (ldind_op) { - case MINT_LDIND_I1: return MINT_TYPE_I1; - case MINT_LDIND_U1: return MINT_TYPE_U1; - case MINT_LDIND_I2: return MINT_TYPE_I2; - case MINT_LDIND_U2: return MINT_TYPE_U2; - case MINT_LDIND_I4: return MINT_TYPE_I4; - case MINT_LDIND_I8: return MINT_TYPE_I8; - case MINT_LDIND_R4: return MINT_TYPE_R4; - case MINT_LDIND_R8: return MINT_TYPE_R8; - default: - g_assert_not_reached (); - } - return -1; -} - static int interp_get_stind_for_mt (int mt) { @@ -1855,15 +1579,15 @@ interp_emit_ldobj (TransformData *td, MonoClass *klass) interp_add_ins (td, MINT_LDOBJ_VT); size = mono_class_value_size (klass, NULL); g_assert (size < G_MAXUINT16); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, klass, size); } else { int opcode = interp_get_ldind_for_mt (mt); interp_add_ins (td, opcode); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type (td, stack_type [mt], klass); } - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); } @@ -1887,9 +1611,9 @@ interp_emit_stobj (TransformData *td, MonoClass *klass, gboolean reverse_order) } td->sp -= 2; if (reverse_order) - interp_ins_set_sregs2 (td->last_ins, td->sp [1].local, td->sp [0].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [1].var, td->sp [0].var); else - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } static void @@ -1906,7 +1630,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check if (!check_class || m_class_is_valuetype (element_class)) { if (rank == 1 && !bounded) { interp_add_ins (td, MINT_LDELEMA1); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); g_assert (size < G_MAXUINT16); td->last_ins->data [0] = GINT_TO_UINT16 (size); } else { @@ -1914,7 +1638,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; } call_args [rank + 1] = -1; g_assert (rank < G_MAXUINT16 && size < G_MAXUINT16); @@ -1930,7 +1654,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; } call_args [rank + 1] = -1; td->last_ins->data [0] = get_data_item_index (td, check_class); @@ -1941,7 +1665,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check } push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static void @@ -1957,9 +1681,9 @@ interp_emit_metadata_update_ldflda (TransformData *td, MonoClassField *field, Mo interp_add_ins (td, MINT_METADATA_UPDATE_LDFLDA); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, field_type); td->last_ins->data [1] = get_data_item_index (td, GUINT_TO_POINTER (field_token)); } @@ -2019,7 +1743,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // the code is unsafe (like in some wrappers). In that case we assume the type // of the array and don't do any checks. - int local = create_interp_local (td, local_type); + int local = interp_create_var (td, local_type); store_local (td, local); interp_emit_ldelema (td, target_method->klass, value_class); @@ -2063,9 +1787,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->last_ins->dreg = ldloca2->sregs [0]; // Remove the ldlocas - td->locals [ldloca1->sregs [0]].indirects--; - td->locals [ldloca2->sregs [0]].indirects--; - mono_interp_stats.ldlocas_removed += 2; + td->vars [ldloca1->sregs [0]].indirects--; + td->vars [ldloca2->sregs [0]].indirects--; interp_clear_ins (ldloca1); interp_clear_ins (ldloca2); td->sp -= 2; @@ -2187,9 +1910,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->last_ins->data [0] = GINT_TO_UINT16 (size); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } @@ -2218,9 +1941,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_SUB_I8); #endif td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [1].local, td->sp [0].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [1].var, td->sp [0].var); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "Unbox")) { @@ -2234,9 +1957,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_UNBOX); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -2266,9 +1989,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas MonoClass *k = mono_defaults.boolean_class; interp_add_ins (td, MINT_CLT_UN_P); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_type (td, stack_type [mono_mint_type (m_class_get_byval_arg (k))], k); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "IsAddressGreaterThan")) { @@ -2279,9 +2002,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_CGT_UN_P); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "SizeOf")) { @@ -2295,7 +2018,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &esize); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "SkipInit")) { @@ -2316,7 +2039,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &offset); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "GetHashCode") || !strcmp (tm, "InternalGetHashCode")) { @@ -2328,9 +2051,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; @@ -2369,7 +2092,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas *op = has_refs ? MINT_LDC_I4_1 : MINT_LDC_I4_0; } else if (!strcmp (tm, "CreateSpan") && csignature->param_count == 1 && - td->last_ins->opcode == MINT_LDPTR && td->last_ins->dreg == td->sp [-1].local) { + td->last_ins->opcode == MINT_LDPTR && td->last_ins->dreg == td->sp [-1].var) { MonoGenericContext* ctx = mono_method_get_context (target_method); g_assert (ctx); g_assert (ctx->method_inst); @@ -2391,16 +2114,16 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // push the length of this span push_simple_type (td, STACK_TYPE_I4); - interp_get_ldc_i4_from_const (td, NULL, num_elements, td->sp [-1].local); + interp_get_ldc_i4_from_const (td, NULL, num_elements, td->sp [-1].var); // create span interp_add_ins (td, MINT_INTRINS_SPAN_CTOR); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); MonoClass *ret_class = mono_class_from_mono_type_internal (csignature->ret); push_type_vt (td, ret_class, mono_class_value_size (ret_class, NULL)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; @@ -2428,7 +2151,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->sp--; interp_add_ins (td, MINT_LDPTR); push_type (td, STACK_TYPE_O, mono_defaults.runtimetype_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, systype); td->ip += 5; @@ -2446,15 +2169,15 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_LDIND_I); } td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_O); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } interp_add_ins (td, MINT_INTRINS_GET_TYPE); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type (td, STACK_TYPE_O, mono_defaults.runtimetype_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); mono_class_init_internal (target_method->klass); @@ -2478,8 +2201,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas base_klass = mono_class_from_mono_type_internal (base_type); // Remove the boxing of valuetypes, by replacing them with moves - prev_prev_ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mono_mint_type (base_type), FALSE)); - td->last_ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mono_mint_type (base_type), FALSE)); + prev_prev_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mono_mint_type (base_type), FALSE)); + td->last_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mono_mint_type (base_type), FALSE)); intrinsify = TRUE; } else if (td->last_ins && td->last_ins->opcode == MINT_BOX && @@ -2493,19 +2216,19 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas int mt = mono_mint_type (m_class_get_byval_arg (base_klass)); // Remove boxing and load the value of this - td->last_ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, FALSE)); + td->last_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, FALSE)); InterpInst *ins = interp_insert_ins (td, prev_prev_ins, interp_get_ldind_for_mt (mt)); - interp_ins_set_sreg (ins, td->sp [-2].local); - interp_ins_set_dreg (ins, td->sp [-2].local); + interp_ins_set_sreg (ins, td->sp [-2].var); + interp_ins_set_dreg (ins, td->sp [-2].var); intrinsify = TRUE; } if (intrinsify) { interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG); td->last_ins->data [0] = get_data_item_index (td, base_klass); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } @@ -2547,8 +2270,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0; if (is_compareto) { int locala, localb; - locala = create_interp_local (td, t); - localb = create_interp_local (td, t); + locala = interp_create_var (td, t); + localb = interp_create_var (td, t); // Save arguments store_local (td, localb); @@ -2560,9 +2283,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas { interp_add_ins (td, MINT_SUB_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { @@ -2572,9 +2295,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas else interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); // (a < b) load_local (td, locala); load_local (td, localb); @@ -2583,15 +2306,15 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas else interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); // (a > b) - (a < b) interp_add_ins (td, MINT_SUB_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; return TRUE; @@ -2661,9 +2384,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, opcode); td->last_ins->data [0] = ct & (is_i4 ? 31 : 63); td->sp -= 2; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, is_i4 ? STACK_TYPE_I4 : STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; @@ -3008,7 +2731,7 @@ interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHe td->inlined_method = target_method; prev_max_stack_height = td->max_stack_height; - prev_locals_size = td->locals_size; + prev_locals_size = td->vars_size; prev_n_data_items = td->n_data_items; prev_in_offsets = td->in_offsets; @@ -3039,7 +2762,7 @@ interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHe if (td->verbose_level) g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name); td->max_stack_height = prev_max_stack_height; - td->locals_size = prev_locals_size; + td->vars_size = prev_locals_size; /* Remove any newly added items */ for (i = prev_n_data_items; i < td->n_data_items; i++) { @@ -3126,7 +2849,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi else vtsize = MINT_STACK_SLOT_SIZE; - dreg = create_interp_local (td, get_type_from_stack (stack_type [ret_mt], klass)); + dreg = interp_create_var (td, get_type_from_stack (stack_type [ret_mt], klass)); // For valuetypes, we need to control the lifetime of the valuetype. // MINT_NEWOBJ_VT_INLINED takes the address of this reg and we should keep @@ -3134,13 +2857,13 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi interp_add_ins (td, MINT_DEF); interp_ins_set_dreg (td->last_ins, dreg); } else { - dreg = create_interp_local (td, get_type_from_stack (stack_type [ret_mt], klass)); + dreg = interp_create_var (td, get_type_from_stack (stack_type [ret_mt], klass)); } // Allocate `this` pointer if (is_vt) { push_simple_type (td, STACK_TYPE_I); - this_reg = td->sp [-1].local; + this_reg = td->sp [-1].var; } else { push_var (td, dreg); } @@ -3208,9 +2931,9 @@ interp_constrained_box (TransformData *td, MonoClass *constrained_class, MonoMet interp_add_ins (td, MINT_BOX_PTR); td->last_ins->data [0] = get_data_item_index (td, vtable); } - interp_ins_set_sreg (td->last_ins, sp->local); - set_simple_type_and_local (td, sp, STACK_TYPE_O); - interp_ins_set_dreg (td->last_ins, sp->local); + interp_ins_set_sreg (td->last_ins, sp->var); + set_simple_type_and_var (td, sp, STACK_TYPE_O); + interp_ins_set_dreg (td->last_ins, sp->var); } static MonoMethod* @@ -3314,7 +3037,7 @@ create_call_args (TransformData *td, int num_args) return NULL; int *call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); for (int i = 0; i < num_args; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [num_args] = -1; return call_args; } @@ -3323,7 +3046,7 @@ static void interp_realign_simd_params (TransformData *td, StackInfo *sp_params, int num_args, int prev_offset) { for (int i = 1; i < num_args; i++) { - if (td->locals [sp_params [i].local].flags & INTERP_LOCAL_FLAG_SIMD) { + if (td->vars [sp_params [i].var].simd) { gint16 offset_amount; // If the simd struct comes immediately after the previous argument we do upper align // otherwise we should lower align to preserve call convention @@ -3427,11 +3150,11 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target *start = sp_fp; } else { int *arg_locals = mono_mempool_alloc0 (td->mempool, sizeof (int) * csignature->param_count); - int fp_local = create_interp_local (td, m_class_get_byval_arg (mono_defaults.int_class)); + int fp_local = interp_create_var (td, m_class_get_byval_arg (mono_defaults.int_class)); // Pop everything into locals. Push after into correct order store_local (td, fp_local); for (int i = csignature->param_count - 1; i >= 0; i--) { - arg_locals [i] = create_interp_local (td, csignature->params [i]); + arg_locals [i] = interp_create_var (td, csignature->params [i]); store_local (td, arg_locals [i]); } load_local (td, fp_local); @@ -3517,9 +3240,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target StackInfo *sp = td->sp - 1 - csignature->param_count; /* managed pointer on the stack, we need to deref that puppy */ interp_add_ins (td, MINT_LDIND_I); - interp_ins_set_sreg (td->last_ins, sp->local); - set_simple_type_and_local (td, sp, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, sp->local); + interp_ins_set_sreg (td->last_ins, sp->var); + set_simple_type_and_var (td, sp, STACK_TYPE_I); + interp_ins_set_dreg (td->last_ins, sp->var); } else if (target_method->klass != constrained_class) { /* * The type parameter is instantiated as a valuetype, @@ -3583,9 +3306,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (is_virtual) { interp_add_ins (td, MINT_CKNULL); - interp_ins_set_sreg (td->last_ins, td->sp->local); - set_simple_type_and_local (td, td->sp, td->sp->type); - interp_ins_set_dreg (td->last_ins, td->sp->local); + interp_ins_set_sreg (td->last_ins, td->sp->var); + set_simple_type_and_var (td, td->sp, td->sp->type); + interp_ins_set_dreg (td->last_ins, td->sp->var); interp_add_ins (td, MINT_TAILCALL_VIRT); td->last_ins->data [2] = get_virt_method_slot (target_method); @@ -3635,9 +3358,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (need_null_check) { StackInfo *sp = td->sp - 1 - csignature->param_count; interp_add_ins (td, MINT_CKNULL); - interp_ins_set_sreg (td->last_ins, sp->local); - set_type_and_local (td, sp, sp->type, sp->klass); - interp_ins_set_dreg (td->last_ins, sp->local); + interp_ins_set_sreg (td->last_ins, sp->var); + set_type_and_var (td, sp, sp->type, sp->klass); + interp_ins_set_dreg (td->last_ins, sp->var); } /* Offset the function pointer when emitting convert instructions */ @@ -3698,7 +3421,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target /* Pop the function pointer */ if (calli) { --td->sp; - fp_sreg = td->sp [0].local; + fp_sreg = td->sp [0].var; } int param_end_offset = 0; @@ -3740,7 +3463,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (calli) { // fp_sreg is at the top of the stack, make sure it is not overwritten by MINT_CALL_ALIGN_STACK int offset = new_param_offset - param_offset; - td->locals [fp_sreg].stack_offset += offset; + td->vars [fp_sreg].stack_offset += offset; } } else { call_offset = ALIGN_TO (param_offset, MINT_STACK_ALIGNMENT); @@ -3751,7 +3474,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target // We overwrite it with the return local, save it for future use if (csignature->param_count || csignature->hasthis) - first_sreg = td->sp [0].local; + first_sreg = td->sp [0].var; /* need to handle typedbyref ... */ if (csignature->ret->type != MONO_TYPE_VOID) { @@ -3772,14 +3495,14 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } else { push_type (td, stack_type[mt], klass); } - dreg = td->sp [-1].local; + dreg = td->sp [-1].var; } else { // Create a new dummy local to serve as the dreg of the call // FIXME Consider adding special dreg type (ex -1), that is // resolved to null offset. The opcode shouldn't really write to it push_simple_type (td, STACK_TYPE_I4); td->sp--; - dreg = td->sp [0].local; + dreg = td->sp [0].var; } if (op >= 0) { @@ -3793,9 +3516,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (num_sregs == 1) interp_ins_set_sreg (td->last_ins, first_sreg); else if (num_sregs == 2) - interp_ins_set_sregs2 (td->last_ins, first_sreg, sp_args [1].local); + interp_ins_set_sregs2 (td->last_ins, first_sreg, sp_args [1].var); else if (num_sregs == 3) - interp_ins_set_sregs3 (td->last_ins, first_sreg, sp_args [1].local, sp_args [2].local); + interp_ins_set_sregs3 (td->last_ins, first_sreg, sp_args [1].var, sp_args [2].var); else g_error ("Unsupported opcode"); } @@ -3822,7 +3545,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target // needs to be able to access the actual arguments to continue with the call so it // needs to know whether there is an empty stack slot between the delegate ptr and the // rest of the args - gboolean first_arg_is_simd = td->locals [sp_args [1].local].flags & INTERP_LOCAL_FLAG_SIMD; + gboolean first_arg_is_simd = td->vars [sp_args [1].var].simd; td->last_ins->data [2] = first_arg_is_simd ? MINT_SIMD_ALIGNMENT : MINT_STACK_SLOT_SIZE; } } else if (calli) { @@ -3958,8 +3681,8 @@ interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, M return field; } -static InterpBasicBlock* -alloc_bb (TransformData *td) +InterpBasicBlock* +interp_alloc_bb (TransformData *td) { InterpBasicBlock *bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock)); bb->il_offset = -1; @@ -3977,7 +3700,7 @@ get_bb (TransformData *td, unsigned char *ip, gboolean make_list) InterpBasicBlock *bb = td->offset_to_bb [offset]; if (!bb) { - bb = alloc_bb (td); + bb = interp_alloc_bb (td); bb->il_offset = offset; td->offset_to_bb [offset] = bb; @@ -4375,9 +4098,9 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet int num_locals = num_args + num_il_locals; imethod->local_offsets = (guint32*)g_malloc (num_il_locals * sizeof(guint32)); - td->locals = (InterpLocal*)g_malloc (num_locals * sizeof (InterpLocal)); - td->locals_size = num_locals; - td->locals_capacity = td->locals_size; + td->vars = (InterpVar*)g_malloc0 (num_locals * sizeof (InterpVar)); + td->vars_size = num_locals; + td->vars_capacity = td->vars_size; offset = 0; /* @@ -4392,15 +4115,15 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet else type = mono_method_signature_internal (td->method)->params [i - sig->hasthis]; int mt = mono_mint_type (type); - td->locals [i].type = type; - td->locals [i].flags = INTERP_LOCAL_FLAG_GLOBAL; - td->locals [i].indirects = 0; - td->locals [i].mt = mt; - td->locals [i].def = NULL; + td->vars [i].type = type; + td->vars [i].global = TRUE; + td->vars [i].indirects = 0; + td->vars [i].mt = mt; + td->vars [i].def = NULL; size = mono_interp_type_size (type, mt, &align); - td->locals [i].size = size; + td->vars [i].size = size; offset = ALIGN_TO (offset, align); - td->locals [i].offset = offset; + td->vars [i].offset = offset; offset += size; } offset = ALIGN_TO (offset, MINT_STACK_ALIGNMENT); @@ -4418,13 +4141,13 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet } offset = ALIGN_TO (offset, align); imethod->local_offsets [i] = offset; - td->locals [index].type = header->locals [i]; - td->locals [index].offset = offset; - td->locals [index].flags = INTERP_LOCAL_FLAG_GLOBAL; - td->locals [index].indirects = 0; - td->locals [index].mt = mono_mint_type (header->locals [i]); - td->locals [index].def = NULL; - td->locals [index].size = size; + td->vars [index].type = header->locals [i]; + td->vars [index].offset = offset; + td->vars [index].global = TRUE; + td->vars [index].indirects = 0; + td->vars [index].mt = mono_mint_type (header->locals [i]); + td->vars [index].def = NULL; + td->vars [index].size = size; // Every local takes a MINT_STACK_SLOT_SIZE so IL locals have same behavior as execution locals offset += size; } @@ -4436,10 +4159,10 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet imethod->clause_data_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32)); td->clause_vars = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * header->num_clauses); for (guint i = 0; i < header->num_clauses; i++) { - int var = create_interp_local (td, mono_get_object_type ()); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; - alloc_global_var_offset (td, var); - imethod->clause_data_offsets [i] = td->locals [var].offset; + int var = interp_create_var (td, mono_get_object_type ()); + td->vars [var].global = TRUE; + interp_alloc_global_var_offset (td, var); + imethod->clause_data_offsets [i] = td->vars [var].offset; td->clause_vars [i] = var; } } @@ -4500,12 +4223,12 @@ interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS); } td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); if (isinst_instr) push_type (td, td->sp [0].type, td->sp [0].klass); else push_type (td, STACK_TYPE_O, klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -4525,11 +4248,11 @@ interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error) g_assert (offset); interp_add_ins (td, MINT_LDTSFLDA); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE32_INS(td->last_ins, 0, &offset); } else { interp_add_ins (td, MINT_LDSFLDA); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, vtable); td->last_ins->data [1] = get_data_item_index (td, mono_static_field_get_addr (vtable, field)); } @@ -4560,21 +4283,21 @@ interp_emit_load_const (TransformData *td, gpointer field_addr, int mt) default: val = *(gint32*)field_addr; } - interp_get_ldc_i4_from_const (td, NULL, val, td->sp [-1].local); + interp_get_ldc_i4_from_const (td, NULL, val, td->sp [-1].var); } else if (mt == MINT_TYPE_I8) { gint64 val = *(gint64*)field_addr; interp_add_ins (td, MINT_LDC_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &val); } else if (mt == MINT_TYPE_R4) { float val = *(float*)field_addr; interp_add_ins (td, MINT_LDC_R4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE32_INS (td->last_ins, 0, &val); } else if (mt == MINT_TYPE_R8) { double val = *(double*)field_addr; interp_add_ins (td, MINT_LDC_R8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &val); } else { // Revert stack @@ -4605,7 +4328,7 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi // Load address of thread static field push_simple_type (td, STACK_TYPE_MP); interp_add_ins (td, MINT_LDTSFLDA); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE32_INS (td->last_ins, 0, &offset); // Do a load/store to this address @@ -4613,17 +4336,17 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi if (mt == MINT_TYPE_VT) { int field_size = mono_class_value_size (field_class, NULL); interp_add_ins (td, MINT_LDOBJ_VT); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); td->sp--; push_type_vt (td, field_class, field_size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT_TO_UINT16 (field_size); } else { interp_add_ins (td, interp_get_ldind_for_mt (mt)); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); td->sp--; push_type (td, stack_type [mt], field_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } } else { interp_emit_stobj (td, field_class, TRUE); @@ -4658,14 +4381,14 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi interp_add_ins (td, MINT_LDSFLD_I1 + mt - MINT_TYPE_I1); push_type (td, stack_type [mt], field_class); } - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { if (G_LIKELY (!wide_data)) interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1)); else interp_add_ins (td, MINT_STSFLD_W); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); } if (G_LIKELY (!wide_data)) { @@ -4716,7 +4439,7 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].type = STACK_TYPE_O; bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; - bb->stack_state [0].local = td->clause_vars [i]; + bb->stack_state [0].var = td->clause_vars [i]; } if (c->flags == MONO_EXCEPTION_CLAUSE_FILTER) { @@ -4728,7 +4451,7 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].type = STACK_TYPE_O; bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; - bb->stack_state [0].local = td->clause_vars [i]; + bb->stack_state [0].var = td->clause_vars [i]; } else if (c->flags == MONO_EXCEPTION_CLAUSE_NONE) { /* * JIT doesn't emit sdb seq intr point at the start of catch clause, probably @@ -4747,9 +4470,9 @@ handle_ldind (TransformData *td, int op, int type, gboolean *volatile_) CHECK_STACK_RET_VOID (td, 1); interp_add_ins (td, op); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (*volatile_) { interp_emit_memory_barrier (td, MONO_MEMORY_BARRIER_ACQ); @@ -4768,7 +4491,7 @@ handle_stind (TransformData *td, int op, gboolean *volatile_) } interp_add_ins (td, op); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); ++td->ip; } @@ -4780,9 +4503,9 @@ handle_ldelem (TransformData *td, int op, int type) ENSURE_I4 (td, 1); interp_add_ins (td, op); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; } @@ -4793,7 +4516,7 @@ handle_stelem (TransformData *td, int op) ENSURE_I4 (td, 2); interp_add_ins (td, op); td->sp -= 3; - interp_ins_set_sregs3 (td->last_ins, td->sp [0].local, td->sp [1].local, td->sp [2].local); + interp_ins_set_sregs3 (td->last_ins, td->sp [0].var, td->sp [1].var, td->sp [2].var); ++td->ip; } @@ -4953,7 +4676,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // first instruction in a vararg method needs to copy the variable arguments // into a special region so they can be accessed by MINT_ARGLIST. This region // is localloc'ed so we have compile time static offsets for all locals/stack. - arglist_local = create_interp_local (td, m_class_get_byval_arg (mono_defaults.int_class)); + arglist_local = interp_create_var (td, m_class_get_byval_arg (mono_defaults.int_class)); interp_add_ins (td, MINT_INIT_ARGLIST); interp_ins_set_dreg (td->last_ins, arglist_local); // This is the offset where the variable args are on stack. After this instruction @@ -5005,8 +4728,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, arg_locals = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32)); /* Allocate locals to store inlined method args from stack */ for (int i = signature->param_count - 1; i >= 0; i--) { - MonoType *type = td->locals [td->sp [-1].local].type; - local = create_interp_local (td, type); + MonoType *type = td->vars [td->sp [-1].var].type; + local = interp_create_var (td, type); arg_locals [i + !!signature->hasthis] = local; store_local (td, local); } @@ -5016,8 +4739,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * If this is value type, it is passed by address and not by value. * Valuetype this local gets integer type MINT_TYPE_I. */ - MonoType *type = td->locals [td->sp [-1].local].type; - local = create_interp_local (td, type); + MonoType *type = td->vars [td->sp [-1].var].type; + local = interp_create_var (td, type); arg_locals [0] = local; store_local (td, local); } @@ -5025,7 +4748,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32)); /* Allocate locals to store inlined method args from stack */ for (int i = 0; i < header->num_locals; i++) - local_locals [i] = create_interp_local (td, header->locals [i]); + local_locals [i] = interp_create_var (td, header->locals [i]); } td->dont_inline = g_list_prepend (td->dont_inline, method); @@ -5070,12 +4793,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int index = td->clause_indexes [in_offset]; if (index != -1 && new_bb->stack_height == 1 && header->clauses [index].handler_offset == in_offset) { int exvar = td->clause_vars [index]; - g_assert (td->stack [0].local == exvar); + g_assert (td->stack [0].var == exvar); td->sp--; push_simple_type (td, STACK_TYPE_O); interp_add_ins (td, MINT_MOV_P); interp_ins_set_sreg (td->last_ins, exvar); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } } } @@ -5195,15 +4918,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!inlining) { interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, n); - td->locals [n].indirects++; + td->vars [n].indirects++; } else { int loc_n = arg_locals [n]; interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, loc_n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; } push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; break; } @@ -5233,9 +4956,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else loc_n = local_locals [loc_n]; interp_ins_set_sreg (td->last_ins, loc_n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; break; } @@ -5251,13 +4974,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_LDNULL: interp_add_ins (td, MINT_LDNULL); push_type (td, STACK_TYPE_O, NULL); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDC_I4_M1: interp_add_ins (td, MINT_LDC_I4_M1); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDC_I4_0: @@ -5265,14 +4988,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) { interp_add_ins (td, MINT_CEQ0_I4); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 3; } else { interp_add_ins (td, MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; } break; @@ -5281,14 +5004,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) { interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; } else { interp_add_ins (td, MINT_LDC_I4_1); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; } break; @@ -5301,14 +5024,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_LDC_I4_8: interp_add_ins (td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDC_I4_S: interp_add_ins (td, MINT_LDC_I4_S); td->last_ins->data [0] = ((gint8 *) td->ip) [1]; push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; break; case CEE_LDC_I4: @@ -5316,7 +5039,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &i32); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; case CEE_LDC_I8: { @@ -5324,7 +5047,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I8); WRITE64_INS (td->last_ins, 0, &val); push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 9; break; } @@ -5334,7 +5057,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_R4); WRITE32_INS (td->last_ins, 0, &val); push_simple_type (td, STACK_TYPE_R4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; } @@ -5344,28 +5067,28 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_R8); WRITE64_INS (td->last_ins, 0, &val); push_simple_type (td, STACK_TYPE_R8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 9; break; } case CEE_DUP: { int type = td->sp [-1].type; klass = td->sp [-1].klass; - mt = td->locals [td->sp [-1].local].mt; + mt = td->vars [td->sp [-1].var].mt; if (mt == MINT_TYPE_VT) { gint32 size = mono_class_value_size (klass, NULL); g_assert (size < G_MAXUINT16); interp_add_ins (td, MINT_MOV_VT); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); push_type_vt (td, klass, size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT32_TO_UINT16 (size); } else { - interp_add_ins (td, get_mov_for_type (mt, FALSE)); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); push_type (td, type, klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip++; break; @@ -5480,7 +5203,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, is_void ? MINT_PROF_EXIT_VOID : MINT_PROF_EXIT); td->last_ins->data [0] = exit_profiling; if (!is_void) { - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); WRITE32_INS (td->last_ins, 1, &vt_size); } } else { @@ -5498,12 +5221,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_RET_U2); else interp_add_ins (td, MINT_RET); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); } } else { interp_add_ins (td, MINT_RET_VT); g_assert (vt_size < G_MAXUINT16); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = GINT_TO_UINT16 (vt_size); } } @@ -5658,7 +5381,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 4; next_ip = td->ip + n * 4; --td->sp; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); InterpBasicBlock **target_bb_table = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * n); for (guint32 i = 0; i < n; i++) { int offset = read32 (td->ip); @@ -6016,7 +5739,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I8); td->sp--; push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &ct); } else { interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_I8_I4); @@ -6087,7 +5810,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I8); td->sp--; push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &ct); } else { interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_I8_U4); @@ -6130,17 +5853,17 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ); td->last_ins->data [0] = get_data_item_index (td, klass); } - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } else { td->sp--; interp_add_ins (td, MINT_LDIND_I); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->sp -= 2; interp_add_ins (td, MINT_STIND_REF); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } td->ip += 5; break; @@ -6172,7 +5895,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, /* GC won't scan code stream, but reference is held by metadata * machinery so we are good here */ interp_add_ins (td, MINT_LDSTR); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, s); } else if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) { /* token is an index into the MonoDynamicMethod:method.method_data @@ -6182,14 +5905,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * the actual data item is a managed MonoString from the managed DynamicMethod */ interp_add_ins (td, MINT_LDSTR_DYNAMIC); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token)); } else { /* the token is an index into MonoWrapperMethod:method_data that * stores a global or malloc'ed C string. defer MonoString * allocation to execution-time */ interp_add_ins (td, MINT_LDSTR_CSTR); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); const char *cstr = (const char*)mono_method_get_wrapper_data (method, token); td->last_ins->data [0] = get_data_item_index (td, (void*)cstr); } @@ -6238,7 +5961,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); td->sp -= csignature->param_count; for (int i = 0; i < csignature->param_count; i++) { - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; } call_args [csignature->param_count] = -1; @@ -6249,7 +5972,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!td->optimized) td->last_ins->info.call_info->call_offset = get_tos_offset (td); push_type (td, stack_type [ret_mt], klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->info.call_info->call_args = call_args; } else if (klass == mono_defaults.string_class) { @@ -6263,15 +5986,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m)); td->last_ins->data [1] = GUINT32_TO_UINT16 (params_stack_size); push_type (td, stack_type [ret_mt], klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 2) * sizeof (int)); td->sp -= csignature->param_count; // First arg is dummy var, it is null when passed to the ctor - call_args [0] = create_interp_local (td, get_type_from_stack (stack_type [ret_mt], NULL)); + call_args [0] = interp_create_var (td, get_type_from_stack (stack_type [ret_mt], NULL)); for (int i = 0; i < csignature->param_count; i++) { - call_args [i + 1] = td->sp [i].local; + call_args [i + 1] = td->sp [i].var; } call_args [csignature->param_count + 1] = -1; @@ -6280,7 +6003,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index_imethod (td, mono_interp_get_imethod (m)); push_type (td, stack_type [ret_mt], klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); init_last_ins_call (td); td->last_ins->info.call_info->call_args = call_args; @@ -6295,9 +6018,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MONO_PROFILER_RAISE (inline_method, (td->rtm->method, m)); interp_add_ins (td, MINT_INTRINS_SPAN_CTOR); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_type_vt (td, klass, mono_class_value_size (klass, NULL)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else if (!td->optimized) { int tos = get_tos_offset (td); int param_offset, param_size; @@ -6333,13 +6056,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); } - int dreg = td->sp [-1].local; + int dreg = td->sp [-1].var; int call_offset = get_tos_offset (td); call_offset = ALIGN_TO (call_offset, MINT_STACK_ALIGNMENT); if (param_count) { - if (td->locals [sp_params [0].local].flags & INTERP_LOCAL_FLAG_SIMD) { + if (td->vars [sp_params [0].var].simd) { // if first arg is simd, move all args at next aligned offset after this ptr interp_add_ins (td, MINT_MOV_STACK_UNOPT); td->last_ins->data [0] = GINT_TO_UINT16 (param_offset); @@ -6410,7 +6133,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); push_type (td, stack_type [ret_mt], klass); } - int dreg = td->sp [-2].local; + int dreg = td->sp [-2].var; // Push back the params to top of stack. The original vars are maintained. ensure_stack (td, csignature->param_count); @@ -6442,7 +6165,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // Save the arguments for the call int *call_args = (int*) mono_mempool_alloc (td->mempool, (csignature->param_count + 2) * sizeof (int)); for (int i = 0; i < csignature->param_count + 1; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [csignature->param_count + 1] = -1; td->last_ins->info.call_info->call_args = call_args; } @@ -6501,20 +6224,20 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type * We create a local variable in the frame so that we can fetch its address. */ - int local = create_interp_local (td, m_class_get_byval_arg (klass)); + int local = interp_create_var (td, m_class_get_byval_arg (klass)); store_local (td, local); interp_add_ins (td, MINT_LDLOCA_S); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, local); - td->locals [local].indirects++; + td->vars [local].indirects++; } else { interp_add_ins (td, MINT_UNBOX); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; } @@ -6541,7 +6264,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // FIXME do this somewhere else, maybe in super instruction pass, where we would check // instruction patterns // Restore the local that is on top of the stack - td->sp [-1].local = td->last_ins->sregs [0]; + td->sp [-1].var = td->last_ins->sregs [0]; td->ip += 5; break; } @@ -6561,9 +6284,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } else { interp_add_ins (td, MINT_UNBOX); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); interp_emit_ldobj (td, klass); @@ -6577,7 +6300,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, INLINE_FAILURE; CHECK_STACK (td, 1); interp_add_ins (td, MINT_THROW); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); link_bblocks = FALSE; td->sp = td->stack; ++td->ip; @@ -6620,9 +6343,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MOV_P); } } - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; } @@ -6661,7 +6384,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MOV_SRC_OFF); g_assert (m_class_is_valuetype (klass)); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = GINT_TO_UINT16 (m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject)); td->last_ins->data [1] = GINT_TO_UINT16 (mt); if (mt == MINT_TYPE_VT) @@ -6672,7 +6395,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type_vt (td, field_klass, field_size); else push_type (td, stack_type [mt], field_klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { if (G_UNLIKELY (m_field_is_from_update (field))) { g_assert (!m_type_is_byref (ftype)); @@ -6691,7 +6414,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, #endif interp_add_ins (td, opcode); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = GINT_TO_UINT16 (m_class_is_valuetype (klass) ? m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject) : m_field_get_offset (field)); if (mt == MINT_TYPE_VT) { int size = mono_class_value_size (field_klass, NULL); @@ -6702,7 +6425,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type_vt (td, field_klass, field_size); else push_type (td, stack_type [mt], field_klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } } td->ip += 5; @@ -6740,7 +6463,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, g_assert (!m_type_is_byref (ftype)); MonoClass *field_class = mono_class_from_mono_type_internal (ftype); MonoType *local_type = m_class_get_byval_arg (field_class); - int local = create_interp_local (td, local_type); + int local = interp_create_var (td, local_type); store_local (td, local); interp_emit_metadata_update_ldflda (td, field, error); goto_if_nok (error, exit); @@ -6756,7 +6479,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, #endif interp_add_ins (td, opcode); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); td->last_ins->data [0] = GINT_TO_UINT16 (m_class_is_valuetype (klass) ? m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject) : m_field_get_offset (field)); if (mt == MINT_TYPE_VT) { /* the vtable of the field might not be initialized at this point */ @@ -6798,7 +6521,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, { interp_add_ins (td, (TARGET_BYTE_ORDER == G_LITTLE_ENDIAN) ? MINT_LDC_I4_1 : MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; } @@ -6926,10 +6649,10 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp--; interp_add_ins (td, vt ? MINT_BOX_VT : MINT_BOX); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = get_data_item_index (td, vtable); push_type (td, STACK_TYPE_O, klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; } @@ -6959,9 +6682,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } td->sp--; interp_add_ins (td, MINT_NEWARR); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type (td, STACK_TYPE_O, array_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, vtable); td->ip += 5; break; @@ -6970,9 +6693,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, 1); td->sp--; interp_add_ins (td, MINT_LDLEN); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDELEMA: { @@ -6998,23 +6721,23 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDELEMA_TC); td->sp -= 2; int *call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); - call_args [0] = td->sp [0].local; - call_args [1] = td->sp [1].local; + call_args [0] = td->sp [0].var; + call_args [1] = td->sp [1].var; call_args [2] = -1; init_last_ins_call (td); if (!td->optimized) td->last_ins->info.call_info->call_offset = get_tos_offset (td); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->last_ins->info.call_info->call_args = call_args; interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); } else { interp_add_ins (td, MINT_LDELEMA1); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); mono_class_init_internal (klass); size = mono_class_array_element_size (klass); td->last_ins->data [0] = GINT32_TO_UINT16 (size); @@ -7098,9 +6821,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, ENSURE_I4 (td, 1); interp_add_ins (td, MINT_LDELEM_VT); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_type_vt (td, klass, size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT_TO_UINT16 (size); ++td->ip; break; @@ -7202,9 +6925,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, g_error ("Invalid stack type"); } td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, ckfinite_stack_type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; } @@ -7217,9 +6940,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MKREFANY); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, mono_defaults.typed_reference_class, sizeof (MonoTypedRef)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -7233,9 +6956,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_REFANYVAL); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -7480,7 +7203,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip = next_next_ip + 5; break; } @@ -7489,13 +7212,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, gpointer systype = mono_type_get_object_checked ((MonoType*)handle, error); goto_if_nok (error, exit); push_type (td, STACK_TYPE_O, mono_defaults.runtimetype_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, systype); td->ip = next_ip + 5; } else { interp_add_ins (td, MINT_LDPTR); push_type_vt (td, klass, sizeof (gpointer)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, handle); td->ip += 5; } @@ -7596,7 +7319,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, 1); interp_add_ins (td, MINT_MONO_RETHROW); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->sp = td->stack; ++td->ip; link_bblocks = FALSE; @@ -7606,19 +7329,19 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, --td->sp; td->ip += 1; interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); break; case CEE_MONO_CALLI_EXTRA_ARG: { - int saved_local = td->sp [-1].local; + int saved_local = td->sp [-1].var; /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */ td->sp -= 2; StackInfo tos = td->sp [1]; // Push back to top of stack and fixup the local offset push_types (td, &tos, 1); - td->sp [-1].local = saved_local; + td->sp [-1].var = saved_local; if (!interp_transform_call (td, method, NULL, generic_context, NULL, FALSE, error, FALSE, FALSE, FALSE)) goto exit; @@ -7631,7 +7354,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDFTN_ADDR); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, (gpointer)func); break; } @@ -7645,19 +7368,19 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp -= info->sig->param_count; int *call_args = (int*)mono_mempool_alloc (td->mempool, (info->sig->param_count + 1) * sizeof (int)); for (int i = 0; i < info->sig->param_count; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [info->sig->param_count] = -1; int param_offset = get_tos_offset (td); if (!MONO_TYPE_IS_VOID (info->sig->ret)) { mt = mono_mint_type (info->sig->ret); push_simple_type (td, stack_type [mt]); - dreg = td->sp [-1].local; + dreg = td->sp [-1].var; } else { // dummy dreg push_simple_type (td, STACK_TYPE_I4); td->sp--; - dreg = td->sp [0].local; + dreg = td->sp [0].var; } if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) { @@ -7692,18 +7415,18 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else size = mono_class_value_size (klass, NULL); - int local = create_interp_local_explicit (td, m_class_get_byval_arg (klass), size); + int local = interp_create_var_explicit (td, m_class_get_byval_arg (klass), size); interp_add_ins (td, MINT_MOV_VT); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); interp_ins_set_dreg (td->last_ins, local); td->last_ins->data [0] = GINT_TO_UINT16 (size); interp_add_ins (td, MINT_LDLOCA_S); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, local); - td->locals [local].indirects++; + td->vars [local].indirects++; ++td->ip; break; @@ -7715,7 +7438,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; interp_add_ins (td, MINT_LDPTR); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token)); break; case CEE_MONO_PINVOKE_ADDR_CACHE: { @@ -7724,7 +7447,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDPTR); g_assert (method->wrapper_type != MONO_WRAPPER_NONE); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); /* This is a memory slot used by the wrapper */ gpointer addr = imethod_alloc0 (td, sizeof (gpointer)); td->last_ins->data [0] = get_data_item_index (td, addr); @@ -7741,7 +7464,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; interp_add_ins (td, MINT_MONO_NEWOBJ); push_simple_type (td, STACK_TYPE_O); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token)); break; case CEE_MONO_RETOBJ: @@ -7750,7 +7473,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; interp_add_ins (td, MINT_MONO_RETOBJ); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); klass = (MonoClass *)mono_method_get_wrapper_data (method, token); /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/ @@ -7767,9 +7490,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int size = mono_class_native_size (klass, NULL); interp_add_ins (td, MINT_LDOBJ_VT); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, klass, size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT_TO_UINT16 (size); break; } @@ -7788,7 +7511,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_MONO_LDPTR_INT_REQ_FLAG: interp_add_ins (td, MINT_LDPTR); push_type (td, STACK_TYPE_MP, NULL); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, &mono_thread_interruption_request_flag); ++td->ip; break; @@ -7799,7 +7522,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_MONO_LDDOMAIN: interp_add_ins (td, MINT_MONO_LDDOMAIN); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_MONO_SAVE_LAST_ERROR: @@ -7822,7 +7545,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // Push a dummy coop gc var interp_add_ins (td, MINT_LDNULL); push_simple_type (td, STACK_TYPE_I); - td->last_ins->dreg = td->sp [-1].local; + td->last_ins->dreg = td->sp [-1].var; interp_add_ins (td, MINT_MONO_ENABLE_GCTRANS); } else { g_assert_checked (jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced); @@ -7869,9 +7592,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4); } td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CGT: @@ -7881,9 +7604,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CGT_UN: @@ -7893,9 +7616,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CLT: @@ -7905,9 +7628,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CLT_UN: @@ -7917,9 +7640,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDVIRTFTN: /* fallthrough */ @@ -7952,7 +7675,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_generate_void_throw (td, MONO_JIT_ICALL_mono_throw_not_supported); interp_add_ins (td, MINT_LDNULL); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; } @@ -7985,7 +7708,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, mono_interp_error_cleanup (wrapper_error); interp_add_ins (td, MINT_LDNULL); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { /* push a pointer to a trampoline that calls m */ gpointer entry = mini_get_interp_callbacks ()->create_method_pointer (m, TRUE, error); @@ -7997,7 +7720,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, WRITE32_INS (td->last_ins, 0, &entry); #endif push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; break; @@ -8009,14 +7732,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, 1); --td->sp; interp_add_ins (td, MINT_LDVIRTFTN); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = index; } else { interp_add_ins (td, MINT_LDFTN); td->last_ins->data [0] = index; } push_simple_type (td, STACK_TYPE_F); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; @@ -8036,15 +7759,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!inlining) { interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, n); - td->locals [n].indirects++; + td->vars [n].indirects++; } else { int loc_n = arg_locals [n]; interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; } push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 3; break; } @@ -8074,9 +7797,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else loc_n = local_locals [loc_n]; interp_ins_set_sreg (td->last_ins, loc_n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 3; break; } @@ -8102,9 +7825,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, mono_error_set_generic_error (error, "System", "InvalidProgramException", ""); goto exit; } - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->has_localloc = TRUE; ++td->ip; break; @@ -8119,7 +7842,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } interp_add_ins (td, MINT_ENDFILTER); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); ++td->ip; link_bblocks = FALSE; break; @@ -8143,18 +7866,18 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (m_class_is_valuetype (klass)) { --td->sp; interp_add_ins (td, MINT_INITOBJ); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); i32 = mono_class_value_size (klass, NULL); g_assert (i32 < G_MAXUINT16); td->last_ins->data [0] = GINT_TO_UINT16 (i32); } else { interp_add_ins (td, MINT_LDNULL); push_type (td, STACK_TYPE_O, NULL); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_add_ins (td, MINT_STIND_REF); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } td->ip += 5; break; @@ -8165,7 +7888,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); interp_add_ins (td, MINT_CPBLK); td->sp -= 3; - interp_ins_set_sregs3 (td->last_ins, td->sp [0].local, td->sp [1].local, td->sp [2].local); + interp_ins_set_sregs3 (td->last_ins, td->sp [0].var, td->sp [1].var, td->sp [2].var); BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_SEQ); ++td->ip; break; @@ -8184,7 +7907,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL); interp_add_ins (td, MINT_INITBLK); td->sp -= 3; - interp_ins_set_sregs3 (td->last_ins, td->sp [0].local, td->sp [1].local, td->sp [2].local); + interp_ins_set_sregs3 (td->last_ins, td->sp [0].var, td->sp [1].var, td->sp [2].var); td->ip += 1; break; case CEE_NO_: @@ -8223,15 +7946,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &size); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); break; } case CEE_REFANYTYPE: interp_add_ins (td, MINT_REFANYTYPE); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, mono_defaults.typehandle_class, mono_class_value_size (mono_defaults.typehandle_class, NULL)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; default: @@ -8331,24 +8054,25 @@ handle_relocations (TransformData *td) } static void -alloc_unopt_global_local (TransformData *td, int local, gpointer data) +alloc_unopt_global_local (TransformData *td, int *plocal, gpointer data) { + int local = *plocal; // Execution stack locals are resolved when we emit the instruction in the code stream, // once all global locals have their offset resolved - if (td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) + if (td->vars [local].execution_stack) return; // Check if already resolved - if (td->locals [local].offset != -1) + if (td->vars [local].offset != -1) return; int offset = td->total_locals_size; - int size = td->locals [local].size; - td->locals [local].offset = offset; + int size = td->vars [local].size; + td->vars [local].offset = offset; td->total_locals_size = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); } -static int -get_inst_length (InterpInst *ins) +int +interp_get_ins_length (InterpInst *ins) { if (ins->opcode == MINT_SWITCH) return MINT_SWITCH_LEN (READ32 (&ins->data [0])); @@ -8360,36 +8084,39 @@ get_inst_length (InterpInst *ins) return mono_interp_oplen [ins->opcode]; } -static void -foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer)) +void +interp_foreach_ins_svar (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)) { int opcode = ins->opcode; if (mono_interp_op_sregs [opcode]) { for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { - int sreg = ins->sregs [i]; - - if (sreg == MINT_CALL_ARGS_SREG) { + if (ins->sregs [i] == MINT_CALL_ARGS_SREG) { if (ins->info.call_info && ins->info.call_info->call_args) { int *call_args = ins->info.call_info->call_args; - int var = *call_args; - while (var != -1) { - callback (td, var, data); + while (*call_args != -1) { + callback (td, call_args, data); call_args++; - var = *call_args; } } } else { - callback (td, sreg, data); + callback (td, &ins->sregs [i], data); } } } +} + +void +interp_foreach_ins_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)) +{ + interp_foreach_ins_svar (td, ins, data, callback); + int opcode = ins->opcode; if (mono_interp_op_dregs [opcode]) - callback (td, ins->dreg, data); + callback (td, &ins->dreg, data); } -static int -compute_native_offset_estimates (TransformData *td) +int +interp_compute_native_offset_estimates (TransformData *td) { InterpBasicBlock *bb; int noe = 0; @@ -8404,9 +8131,9 @@ compute_native_offset_estimates (TransformData *td) // Skip dummy opcodes for more precise offset computation if (MINT_IS_NOP (opcode)) continue; - noe += get_inst_length (ins); + noe += interp_get_ins_length (ins); if (!td->optimized) - foreach_local_var (td, ins, NULL, alloc_unopt_global_local); + interp_foreach_ins_var (td, ins, NULL, alloc_unopt_global_local); } } @@ -8417,8 +8144,8 @@ compute_native_offset_estimates (TransformData *td) return noe; } -static gboolean -is_short_offset (int src_offset, int dest_offset) +gboolean +interp_is_short_offset (int src_offset, int dest_offset) { int diff = dest_offset - src_offset; if (diff >= G_MININT16 && diff <= G_MAXINT16) @@ -8453,8 +8180,8 @@ get_short_brop (int opcode) static int get_local_offset (TransformData *td, int local) { - if (td->locals [local].offset != -1) - return td->locals [local].offset; + if (td->vars [local].offset != -1) + return td->vars [local].offset; // FIXME Some vars might end up with unitialized offset because they are not declared at all in the code. // This can happen if the bblock declaring the var gets removed, while other unreachable bblocks, that access @@ -8466,10 +8193,10 @@ get_local_offset (TransformData *td, int local) // If we use the optimized offset allocator, all locals should have had their offsets already allocated g_assert (!td->optimized); // The only remaining locals to allocate are the ones from the execution stack - g_assert (td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK); + g_assert (td->vars [local].execution_stack); - td->locals [local].offset = td->total_locals_size + td->locals [local].stack_offset; - return td->locals [local].offset; + td->vars [local].offset = td->total_locals_size + td->vars [local].stack_offset; + return td->vars [local].offset; } static guint16* @@ -8531,7 +8258,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in if (ins->info.target_bb->native_offset >= 0) { int offset = ins->info.target_bb->native_offset - br_offset; // Backwards branch. We can already patch it. - if (is_short_offset (br_offset, ins->info.target_bb->native_offset)) { + if (interp_is_short_offset (br_offset, ins->info.target_bb->native_offset)) { // Replace the long opcode we added at the start *start_ip = GINT_TO_OPCODE (get_short_brop (opcode)); *ip++ = GINT_TO_UINT16 (ins->info.target_bb->native_offset - br_offset); @@ -8543,7 +8270,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in ip--; } else { // If the estimate offset is short, then surely the real offset is short - gboolean is_short = is_short_offset (br_offset, ins->info.target_bb->native_offset_estimate); + gboolean is_short = interp_is_short_offset (br_offset, ins->info.target_bb->native_offset_estimate); if (is_short) *start_ip = GINT_TO_OPCODE (get_short_brop (opcode)); @@ -8604,7 +8331,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in } else { if (opcode == MINT_MOV_SRC_OFF) { // Loading from field, always load full i4 - opcode = GINT_TO_OPCODE (get_mov_for_type (mt, TRUE)); + opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, TRUE)); } else { // Storing into field, copy exact size fsize = get_mint_type_size (mt); @@ -8678,7 +8405,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in *ip++ = GINT_TO_UINT16 (get_local_offset (td, ins->sregs [0])); } - int left = get_inst_length (ins) - GPTRDIFF_TO_INT(ip - start_ip); + int left = interp_get_ins_length (ins) - GPTRDIFF_TO_INT(ip - start_ip); // Emit the rest of the data for (int i = 0; i < left; i++) *ip++ = ins->data [i]; @@ -8717,7 +8444,7 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) // This iteration could be avoided at the cost of less precise size result, following // super instruction pass - size = compute_native_offset_estimates (td); + size = interp_compute_native_offset_estimates (td); // Generate the compacted stream of instructions td->new_code = ip = (guint16*)imethod_alloc0 (td, size * sizeof (guint16)); @@ -8789,2314 +8516,6 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) #endif } -static void -interp_mark_reachable_bblocks (TransformData *td) -{ - InterpBasicBlock **queue = mono_mempool_alloc0 (td->mempool, td->bb_count * sizeof (InterpBasicBlock*)); - InterpBasicBlock *current; - int cur_index = 0; - int next_position = 0; - - // FIXME There is no need to force eh bblocks to remain alive - current = td->entry_bb; - while (current != NULL) { - if (current->eh_block || current->patchpoint_data) { - queue [next_position++] = current; - current->reachable = TRUE; - } else { - current->reachable = FALSE; - } - current = current->next_bb; - } - - queue [next_position++] = td->entry_bb; - td->entry_bb->reachable = TRUE; - - // We have the roots, traverse everything else - while (cur_index < next_position) { - current = queue [cur_index++]; - for (int i = 0; i < current->out_count; i++) { - InterpBasicBlock *child = current->out_bb [i]; - if (!child->reachable) { - queue [next_position++] = child; - child->reachable = TRUE; - } - } - } -} - -/** - * Returns TRUE if instruction or previous instructions defines at least one of the variables, FALSE otherwise. - */ - -static gboolean -interp_prev_block_defines_var (InterpInst *ins, int var1, int var2) -{ - // Check max of 5 instructions - for (int i = 0; i < 5; i++) { - ins = interp_prev_ins (ins); - if (!ins) - return FALSE; - if (mono_interp_op_dregs [ins->opcode] && (ins->dreg == var1 || ins->dreg == var2)) - return TRUE; - } - return FALSE; -} - -/** - * Check if the given basic block has a known pattern for inlining into callers blocks, if so, return a pointer to the conditional branch instruction. - * - * The known patterns are: - * - `branch`: a conditional branch instruction. - * - `ldc; branch`: a load instruction followed by a binary conditional branch. - * - `ldc; compare; branch`: a load instruction followed by a compare instruction and a unary conditional branch. - */ -static InterpInst* -interp_inline_into_callers (InterpInst *first, int *lookup_var1, int *lookup_var2) { - // pattern `branch` - if (MINT_IS_CONDITIONAL_BRANCH (first->opcode)) { - *lookup_var1 = first->sregs [0]; - *lookup_var2 = (mono_interp_op_dregs [first->opcode] > 1) ? first->sregs [1] : -1; - return first; - } - - if (MINT_IS_LDC_I4 (first->opcode)) { - InterpInst *second = interp_next_ins (first); - if (!second) - return NULL; - *lookup_var2 = -1; - gboolean first_var_defined = first->dreg == second->sregs [0]; - gboolean second_var_defined = first->dreg == second->sregs [1]; - // pattern `ldc; binop conditional branch` - if (MINT_IS_BINOP_CONDITIONAL_BRANCH (second->opcode) && (first_var_defined || second_var_defined)) { - *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; - return second; - } - - InterpInst *third = interp_next_ins (second); - if (!third) - return NULL; - // pattern `ldc; compare; conditional branch` - if (MINT_IS_COMPARE (second->opcode) && (first_var_defined || second_var_defined) - && MINT_IS_UNOP_CONDITIONAL_BRANCH (third->opcode) && second->dreg == third->sregs [0]) { - *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; - return third; - } - } - - return NULL; -} - -static void -interp_reorder_bblocks (TransformData *td) -{ - InterpBasicBlock *bb; - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - if (bb->eh_block) - continue; - InterpInst *first = interp_first_ins (bb); - if (!first) - continue; - int lookup_var1, lookup_var2; - InterpInst *cond_ins = interp_inline_into_callers (first, &lookup_var1, &lookup_var2); - if (cond_ins) { - // This means this bblock match a pattern for inlining into callers, with a conditional branch - int i = 0; - while (i < bb->in_count) { - InterpBasicBlock *in_bb = bb->in_bb [i]; - InterpInst *last_ins = interp_last_ins (in_bb); - if (last_ins && last_ins->opcode == MINT_BR && interp_prev_block_defines_var (last_ins, lookup_var1, lookup_var2)) { - // This bblock is reached unconditionally from one of its parents - // Move the conditional branch inside the parent to facilitate propagation - // of condition value. - InterpBasicBlock *cond_true_bb = cond_ins->info.target_bb; - InterpBasicBlock *next_bb = bb->next_bb; - - // Parent bb will do the conditional branch - interp_unlink_bblocks (in_bb, bb); - // Remove ending MINT_BR - interp_clear_ins (last_ins); - // Copy all instructions one by one, from interp_first_ins (bb) to the end of the in_bb - InterpInst *copy_ins = first; - while (copy_ins) { - InterpInst *new_ins = interp_insert_ins_bb (td, in_bb, in_bb->last_ins, copy_ins->opcode); - new_ins->dreg = copy_ins->dreg; - new_ins->sregs [0] = copy_ins->sregs [0]; - if (mono_interp_op_sregs [copy_ins->opcode] > 1) - new_ins->sregs [1] = copy_ins->sregs [1]; - - new_ins->data [0] = copy_ins->data [0]; - if (copy_ins->opcode == MINT_LDC_I4) - new_ins->data [1] = copy_ins->data [1]; - - copy_ins = interp_next_ins (copy_ins); - } - in_bb->last_ins->info.target_bb = cond_true_bb; - interp_link_bblocks (td, in_bb, cond_true_bb); - - // Create new fallthrough bb between in_bb and in_bb->next_bb - InterpBasicBlock *new_bb = alloc_bb (td); - new_bb->next_bb = in_bb->next_bb; - in_bb->next_bb = new_bb; - new_bb->il_offset = in_bb->il_offset; - interp_link_bblocks (td, in_bb, new_bb); - - InterpInst *new_inst = interp_insert_ins_bb (td, new_bb, NULL, MINT_BR); - new_inst->info.target_bb = next_bb; - - interp_link_bblocks (td, new_bb, next_bb); - if (td->verbose_level) { - GString* bb_info = get_interp_bb_links (bb); - GString* in_bb_info = get_interp_bb_links (in_bb); - GString* new_bb_info = get_interp_bb_links (new_bb); - g_print ("Moved cond branch BB%d into BB%d, new BB%d\n", bb->index, in_bb->index, new_bb->index); - g_print ("\tBB%d: %s\n", bb->index, bb_info->str); - g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); - g_print ("\tBB%d: %s\n", new_bb->index, new_bb_info->str); - g_string_free (bb_info, TRUE); - g_string_free (in_bb_info, TRUE); - g_string_free (new_bb_info, TRUE); - } - // Since we changed links, in_bb might have changed, loop again from the start - i = 0; - } else { - i++; - } - } - } else if (first->opcode == MINT_BR) { - // All bblocks jumping into this bblock can jump directly into the br target since it is the single instruction of the bb - int i = 0; - while (i < bb->in_count) { - InterpBasicBlock *in_bb = bb->in_bb [i]; - InterpInst *last_ins = interp_last_ins (in_bb); - if (last_ins && (MINT_IS_CONDITIONAL_BRANCH (last_ins->opcode) || - MINT_IS_UNCONDITIONAL_BRANCH (last_ins->opcode)) && - last_ins->info.target_bb == bb) { - InterpBasicBlock *target_bb = first->info.target_bb; - last_ins->info.target_bb = target_bb; - interp_unlink_bblocks (in_bb, bb); - interp_link_bblocks (td, in_bb, target_bb); - if (td->verbose_level) { - GString* bb_info = get_interp_bb_links (bb); - GString* in_bb_info = get_interp_bb_links (in_bb); - GString* target_bb_info = get_interp_bb_links (target_bb); - g_print ("Propagated target bb BB%d into BB%d\n", target_bb->index, in_bb->index); - g_print ("\tBB%d: %s\n", bb->index, bb_info->str); - g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); - g_print ("\tBB%d: %s\n", target_bb->index, target_bb_info->str); - g_string_free (bb_info, TRUE); - g_string_free (in_bb_info, TRUE); - g_string_free (target_bb_info, TRUE); - } - i = 0; - } else { - i++; - } - } - } - } -} - -// Traverse the list of basic blocks and merge adjacent blocks -static gboolean -interp_optimize_bblocks (TransformData *td) -{ - InterpBasicBlock *bb = td->entry_bb; - gboolean needs_cprop = FALSE; - - interp_reorder_bblocks (td); - - interp_mark_reachable_bblocks (td); - - while (TRUE) { - InterpBasicBlock *next_bb = bb->next_bb; - if (!next_bb) - break; - if (!next_bb->reachable) { - if (td->verbose_level) - g_print ("Removed BB%d\n", next_bb->index); - needs_cprop |= interp_remove_bblock (td, next_bb, bb); - continue; - } else if (bb->out_count == 1 && bb->out_bb [0] == next_bb && next_bb->in_count == 1 && !next_bb->eh_block && !next_bb->patchpoint_data) { - g_assert (next_bb->in_bb [0] == bb); - interp_merge_bblocks (td, bb, next_bb); - if (td->verbose_level) - g_print ("Merged BB%d and BB%d\n", bb->index, next_bb->index); - needs_cprop = TRUE; - continue; - } - - bb = next_bb; - } - return needs_cprop; -} - -static gboolean -interp_local_deadce (TransformData *td) -{ - int *local_ref_count = td->local_ref_count; - gboolean needs_dce = FALSE; - gboolean needs_cprop = FALSE; - - for (unsigned int i = 0; i < td->locals_size; i++) { - g_assert (local_ref_count [i] >= 0); - g_assert (td->locals [i].indirects >= 0); - if (td->locals [i].indirects || (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD)) - continue; - if (!local_ref_count [i]) { - needs_dce = TRUE; - td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD; - } else if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_UNKNOWN_USE)) { - if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) { - // The value of this var is not passed between multiple basic blocks - td->locals [i].flags |= INTERP_LOCAL_FLAG_LOCAL_ONLY; - if (td->verbose_level) - g_print ("Var %d is local only\n", i); - needs_cprop = TRUE; - } - } - td->locals [i].flags &= ~INTERP_LOCAL_FLAG_UNKNOWN_USE; - } - - // Return early if all locals are alive - if (!needs_dce) - return needs_cprop; - - // Kill instructions that don't use stack and are storing into dead locals - for (InterpBasicBlock *bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { - if (MINT_NO_SIDE_EFFECTS (ins->opcode) || - ins->opcode == MINT_LDLOCA_S) { - int dreg = ins->dreg; - if (td->locals [dreg].flags & INTERP_LOCAL_FLAG_DEAD) { - if (td->verbose_level) { - g_print ("kill dead ins:\n\t"); - dump_interp_inst (ins, td->data_items); - } - - if (ins->opcode == MINT_LDLOCA_S) { - mono_interp_stats.ldlocas_removed++; - td->locals [ins->sregs [0]].indirects--; - if (!td->locals [ins->sregs [0]].indirects) { - // We can do cprop now through this local. Run cprop again. - needs_cprop = TRUE; - } - } - interp_clear_ins (ins); - mono_interp_stats.killed_instructions++; - // FIXME This is lazy. We should update the ref count for the sregs and redo deadce. - needs_cprop = TRUE; - } - } - } - } - return needs_cprop; -} - -#define INTERP_FOLD_UNOP(opcode,val_type,field,op) \ - case opcode: \ - result.type = val_type; \ - result.field = op val->field; \ - break; - -#define INTERP_FOLD_CONV(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type) \ - case opcode: \ - result.type = val_type_dst; \ - result.field_dst = (cast_type)val->field_src; \ - break; - -#define INTERP_FOLD_CONV_FULL(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type,cond) \ - case opcode: \ - if (!(cond)) return ins; \ - result.type = val_type_dst; \ - result.field_dst = (cast_type)val->field_src; \ - break; - -static InterpInst* -interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - // ins should be an unop, therefore it should have a single dreg and a single sreg - int dreg = ins->dreg; - int sreg = ins->sregs [0]; - LocalValue *val = &local_defs [sreg]; - LocalValue result; - - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8) - return ins; - - // Top of the stack is a constant - switch (ins->opcode) { - INTERP_FOLD_UNOP (MINT_ADD1_I4, LOCAL_VALUE_I4, i, 1+); - INTERP_FOLD_UNOP (MINT_ADD1_I8, LOCAL_VALUE_I8, l, 1+); - INTERP_FOLD_UNOP (MINT_SUB1_I4, LOCAL_VALUE_I4, i, -1+); - INTERP_FOLD_UNOP (MINT_SUB1_I8, LOCAL_VALUE_I8, l, -1+); - INTERP_FOLD_UNOP (MINT_NEG_I4, LOCAL_VALUE_I4, i, -); - INTERP_FOLD_UNOP (MINT_NEG_I8, LOCAL_VALUE_I8, l, -); - INTERP_FOLD_UNOP (MINT_NOT_I4, LOCAL_VALUE_I4, i, ~); - INTERP_FOLD_UNOP (MINT_NOT_I8, LOCAL_VALUE_I8, l, ~); - INTERP_FOLD_UNOP (MINT_CEQ0_I4, LOCAL_VALUE_I4, i, 0 ==); - - INTERP_FOLD_CONV (MINT_CONV_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8); - INTERP_FOLD_CONV (MINT_CONV_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8); - INTERP_FOLD_CONV (MINT_CONV_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8); - INTERP_FOLD_CONV (MINT_CONV_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8); - - INTERP_FOLD_CONV (MINT_CONV_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16); - INTERP_FOLD_CONV (MINT_CONV_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16); - INTERP_FOLD_CONV (MINT_CONV_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16); - INTERP_FOLD_CONV (MINT_CONV_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16); - - INTERP_FOLD_CONV (MINT_CONV_I8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, gint32); - INTERP_FOLD_CONV (MINT_CONV_I8_U4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint32); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= G_MININT8 && val->i <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= G_MININT8 && val->l <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= 0 && val->i <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= 0 && val->l <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8, val->i >= 0 && val->i <= G_MAXUINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8, val->l >= 0 && val->l <= G_MAXUINT8); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= G_MININT16 && val->i <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, i, gint16, val->l >= G_MININT16 && val->l <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= 0 && val->i <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16, val->l >= 0 && val->l <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16, val->i >= 0 && val->i <= G_MAXUINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16, val->l >= 0 && val->l <= G_MAXUINT16); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint32, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= G_MININT32 && val->l <= G_MAXINT32); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= 0 && val->l <= G_MAXINT32); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint32, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint32, val->l >= 0 && val->l <= G_MAXINT32); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I8_U8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, gint64, val->l >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint64, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, guint64, val->l >= 0); - - default: - return ins; - } - - // We were able to compute the result of the ins instruction. We replace the unop - // with a LDC of the constant. We leave alone the sregs of this instruction, for - // deadce to kill the instructions initializing them. - mono_interp_stats.constant_folds++; - - if (result.type == LOCAL_VALUE_I4) - ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); - else if (result.type == LOCAL_VALUE_I8) - ins = interp_inst_replace_with_i8_const (td, ins, result.l); - else - g_assert_not_reached (); - - if (td->verbose_level) { - g_print ("Fold unop :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - local_ref_count [sreg]--; - result.ins = ins; - result.ref_count = 0; - local_defs [dreg] = result; - - return ins; -} - -#define INTERP_FOLD_UNOP_BR(_opcode,_cond) \ - case _opcode: \ - if (_cond) { \ - ins->opcode = MINT_BR; \ - if (cbb->next_bb != ins->info.target_bb) \ - interp_unlink_bblocks (cbb, cbb->next_bb); \ - for (InterpInst *it = ins->next; it != NULL; it = it->next) \ - interp_clear_ins (it); \ - } else { \ - interp_clear_ins (ins); \ - interp_unlink_bblocks (cbb, ins->info.target_bb); \ - } \ - break; - -static InterpInst* -interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - // ins should be an unop conditional branch, therefore it should have a single sreg - int sreg = ins->sregs [0]; - LocalValue *val = &local_defs [sreg]; - - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_NON_NULL) - return ins; - - if (val->type == LOCAL_VALUE_NON_NULL) { - switch (ins->opcode) { - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, FALSE); - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, FALSE); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, TRUE); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, TRUE); - - default: - return ins; - } - } else { - // Top of the stack is a constant - switch (ins->opcode) { - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, val->i == 0); - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, val->l == 0); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, val->i != 0); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, val->l != 0); - - default: - return ins; - } - } - - if (td->verbose_level) { - g_print ("Fold unop cond br :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - mono_interp_stats.constant_folds++; - local_ref_count [sreg]--; - return ins; -} - -#define INTERP_FOLD_BINOP(opcode,local_type,field,op) \ - case opcode: \ - result.type = local_type; \ - result.field = val1->field op val2->field; \ - break; - -#define INTERP_FOLD_BINOP_FULL(opcode,local_type,field,op,cast_type,cond) \ - case opcode: \ - if (!(cond)) return ins; \ - result.type = local_type; \ - result.field = (cast_type)val1->field op (cast_type)val2->field; \ - break; - -#define INTERP_FOLD_SHIFTOP(opcode,local_type,field,shift_op,cast_type) \ - case opcode: \ - result.type = local_type; \ - result.field = (cast_type)val1->field shift_op val2->i; \ - break; - -#define INTERP_FOLD_RELOP(opcode,local_type,field,relop,cast_type) \ - case opcode: \ - result.type = LOCAL_VALUE_I4; \ - result.i = (cast_type) val1->field relop (cast_type) val2->field; \ - break; - - -static InterpInst* -interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, gboolean *folded) -{ - int *local_ref_count = td->local_ref_count; - // ins should be a binop, therefore it should have a single dreg and two sregs - int dreg = ins->dreg; - int sreg1 = ins->sregs [0]; - int sreg2 = ins->sregs [1]; - LocalValue *val1 = &local_defs [sreg1]; - LocalValue *val2 = &local_defs [sreg2]; - LocalValue result; - - *folded = FALSE; - - if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) - return ins; - if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) - return ins; - - // Top two values of the stack are constants - switch (ins->opcode) { - INTERP_FOLD_BINOP (MINT_ADD_I4, LOCAL_VALUE_I4, i, +); - INTERP_FOLD_BINOP (MINT_ADD_I8, LOCAL_VALUE_I8, l, +); - INTERP_FOLD_BINOP (MINT_SUB_I4, LOCAL_VALUE_I4, i, -); - INTERP_FOLD_BINOP (MINT_SUB_I8, LOCAL_VALUE_I8, l, -); - INTERP_FOLD_BINOP (MINT_MUL_I4, LOCAL_VALUE_I4, i, *); - INTERP_FOLD_BINOP (MINT_MUL_I8, LOCAL_VALUE_I8, l, *); - - INTERP_FOLD_BINOP (MINT_AND_I4, LOCAL_VALUE_I4, i, &); - INTERP_FOLD_BINOP (MINT_AND_I8, LOCAL_VALUE_I8, l, &); - INTERP_FOLD_BINOP (MINT_OR_I4, LOCAL_VALUE_I4, i, |); - INTERP_FOLD_BINOP (MINT_OR_I8, LOCAL_VALUE_I8, l, |); - INTERP_FOLD_BINOP (MINT_XOR_I4, LOCAL_VALUE_I4, i, ^); - INTERP_FOLD_BINOP (MINT_XOR_I8, LOCAL_VALUE_I8, l, ^); - - INTERP_FOLD_SHIFTOP (MINT_SHL_I4, LOCAL_VALUE_I4, i, <<, gint32); - INTERP_FOLD_SHIFTOP (MINT_SHL_I8, LOCAL_VALUE_I8, l, <<, gint64); - INTERP_FOLD_SHIFTOP (MINT_SHR_I4, LOCAL_VALUE_I4, i, >>, gint32); - INTERP_FOLD_SHIFTOP (MINT_SHR_I8, LOCAL_VALUE_I8, l, >>, gint64); - INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, LOCAL_VALUE_I4, i, >>, guint32); - INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, LOCAL_VALUE_I8, l, >>, guint64); - - INTERP_FOLD_RELOP (MINT_CEQ_I4, LOCAL_VALUE_I4, i, ==, gint32); - INTERP_FOLD_RELOP (MINT_CEQ_I8, LOCAL_VALUE_I8, l, ==, gint64); - INTERP_FOLD_RELOP (MINT_CNE_I4, LOCAL_VALUE_I4, i, !=, gint32); - INTERP_FOLD_RELOP (MINT_CNE_I8, LOCAL_VALUE_I8, l, !=, gint64); - - INTERP_FOLD_RELOP (MINT_CGT_I4, LOCAL_VALUE_I4, i, >, gint32); - INTERP_FOLD_RELOP (MINT_CGT_I8, LOCAL_VALUE_I8, l, >, gint64); - INTERP_FOLD_RELOP (MINT_CGT_UN_I4, LOCAL_VALUE_I4, i, >, guint32); - INTERP_FOLD_RELOP (MINT_CGT_UN_I8, LOCAL_VALUE_I8, l, >, guint64); - - INTERP_FOLD_RELOP (MINT_CGE_I4, LOCAL_VALUE_I4, i, >=, gint32); - INTERP_FOLD_RELOP (MINT_CGE_I8, LOCAL_VALUE_I8, l, >=, gint64); - INTERP_FOLD_RELOP (MINT_CGE_UN_I4, LOCAL_VALUE_I4, i, >=, guint32); - INTERP_FOLD_RELOP (MINT_CGE_UN_I8, LOCAL_VALUE_I8, l, >=, guint64); - - INTERP_FOLD_RELOP (MINT_CLT_I4, LOCAL_VALUE_I4, i, <, gint32); - INTERP_FOLD_RELOP (MINT_CLT_I8, LOCAL_VALUE_I8, l, <, gint64); - INTERP_FOLD_RELOP (MINT_CLT_UN_I4, LOCAL_VALUE_I4, i, <, guint32); - INTERP_FOLD_RELOP (MINT_CLT_UN_I8, LOCAL_VALUE_I8, l, <, guint64); - - INTERP_FOLD_RELOP (MINT_CLE_I4, LOCAL_VALUE_I4, i, <=, gint32); - INTERP_FOLD_RELOP (MINT_CLE_I8, LOCAL_VALUE_I8, l, <=, gint64); - INTERP_FOLD_RELOP (MINT_CLE_UN_I4, LOCAL_VALUE_I4, i, <=, guint32); - INTERP_FOLD_RELOP (MINT_CLE_UN_I8, LOCAL_VALUE_I8, l, <=, guint64); - - INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, LOCAL_VALUE_I4, i, /, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); - INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, LOCAL_VALUE_I8, l, /, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); - INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, LOCAL_VALUE_I4, i, /, guint32, val2->i != 0); - INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, LOCAL_VALUE_I8, l, /, guint64, val2->l != 0); - - INTERP_FOLD_BINOP_FULL (MINT_REM_I4, LOCAL_VALUE_I4, i, %, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); - INTERP_FOLD_BINOP_FULL (MINT_REM_I8, LOCAL_VALUE_I8, l, %, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); - INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, LOCAL_VALUE_I4, i, %, guint32, val2->i != 0); - INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, LOCAL_VALUE_I8, l, %, guint64, val2->l != 0); - - default: - return ins; - } - - // We were able to compute the result of the ins instruction. We replace the binop - // with a LDC of the constant. We leave alone the sregs of this instruction, for - // deadce to kill the instructions initializing them. - mono_interp_stats.constant_folds++; - *folded = TRUE; - if (result.type == LOCAL_VALUE_I4) - ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); - else if (result.type == LOCAL_VALUE_I8) - ins = interp_inst_replace_with_i8_const (td, ins, result.l); - else - g_assert_not_reached (); - - if (td->verbose_level) { - g_print ("Fold binop :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - local_ref_count [sreg1]--; - local_ref_count [sreg2]--; - result.ins = ins; - result.ref_count = 0; - local_defs [dreg] = result; - return ins; -} - -// Due to poor current design, the branch op might not be the last instruction in the bblock -// (in case we fallthrough and need to have the stack locals match the ones from next_bb, done -// in fixup_newbb_stack_locals). If that's the case, clear all these mov's. This helps bblock -// merging quickly find the MINT_BR opcode. -#define INTERP_FOLD_BINOP_BR(_opcode,_local_type,_cond) \ - case _opcode: \ - if (_cond) { \ - ins->opcode = MINT_BR; \ - if (cbb->next_bb != ins->info.target_bb) \ - interp_unlink_bblocks (cbb, cbb->next_bb); \ - for (InterpInst *it = ins->next; it != NULL; it = it->next) \ - interp_clear_ins (it); \ - } else { \ - interp_clear_ins (ins); \ - interp_unlink_bblocks (cbb, ins->info.target_bb); \ - } \ - break; - -static InterpInst* -interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - // ins should be a conditional binop, therefore it should have only two sregs - int sreg1 = ins->sregs [0]; - int sreg2 = ins->sregs [1]; - LocalValue *val1 = &local_defs [sreg1]; - LocalValue *val2 = &local_defs [sreg2]; - - if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) - return ins; - if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) - return ins; - - switch (ins->opcode) { - INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, LOCAL_VALUE_I4, val1->i == val2->i); - INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, LOCAL_VALUE_I8, val1->l == val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_I4, LOCAL_VALUE_I4, val1->i >= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_I8, LOCAL_VALUE_I8, val1->l >= val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_I4, LOCAL_VALUE_I4, val1->i > val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_I8, LOCAL_VALUE_I8, val1->l > val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_I4, LOCAL_VALUE_I4, val1->i < val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_I8, LOCAL_VALUE_I8, val1->l < val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_I4, LOCAL_VALUE_I4, val1->i <= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_I8, LOCAL_VALUE_I8, val1->l <= val2->l); - - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, LOCAL_VALUE_I4, val1->i != val2->i); - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, LOCAL_VALUE_I8, val1->l != val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i > (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l > (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i < (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l < (guint64)val2->l); - - default: - return ins; - } - if (td->verbose_level) { - g_print ("Fold binop cond br :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - mono_interp_stats.constant_folds++; - local_ref_count [sreg1]--; - local_ref_count [sreg2]--; - return ins; -} - -static void -write_v128_element (gpointer v128_addr, LocalValue *val, int index, int el_size) -{ - gpointer el_addr = (gint8*)v128_addr + index * el_size; - g_assert ((gint8*)el_addr < ((gint8*)v128_addr + 16)); - switch (el_size) { - case 1: *(gint8*)el_addr = (gint8)val->i; break; - case 2: *(gint16*)el_addr = (gint16)val->i; break; - case 4: *(gint32*)el_addr = val->i; break; // this also handles r4 - case 8: *(gint64*)el_addr = val->l; break; - default: - g_assert_not_reached (); - } -} - -static InterpInst* -interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - - int *args = ins->info.call_info->call_args; - int index = 0; - int var = args [index]; - while (var != -1) { - LocalValue *val = &local_defs [var]; - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_R4) - return ins; - index++; - var = args [index]; - } - - // If we reached this point, it means that all args of the simd_create are constants - // We can replace the simd_create with simd_ldc - int el_size = 16 / index; - int dreg = ins->dreg; - - ins = interp_insert_ins (td, ins, MINT_SIMD_V128_LDC); - interp_clear_ins (ins->prev); - interp_ins_set_dreg (ins, dreg); - - gpointer v128_addr = &ins->data [0]; - - index = 0; - var = args [index]; - while (var != -1) { - LocalValue *val = &local_defs [var]; - write_v128_element (v128_addr, val, index, el_size); - val->ref_count--; - local_ref_count [var]--; - index++; - var = args [index]; - } - - if (td->verbose_level) { - g_print ("Fold simd create:\n\t"); - dump_interp_inst (ins, td->data_items); - } - - local_defs [dreg].ins = ins; - local_defs [dreg].type = LOCAL_VALUE_NONE; - - return ins; -} - -static void -cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_defs) -{ - int *local_ref_count = td->local_ref_count; - int sreg = *psreg; - - local_ref_count [sreg]++; - local_defs [sreg].ref_count++; - if (local_defs [sreg].type == LOCAL_VALUE_LOCAL) { - int cprop_local = local_defs [sreg].local; - - // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been - // modified, so we can't use it. - if (local_defs [cprop_local].ins != NULL && local_defs [cprop_local].def_index > local_defs [sreg].def_index) - return; - - if (td->verbose_level) - g_print ("cprop %d -> %d:\n\t", sreg, cprop_local); - local_ref_count [sreg]--; - *psreg = cprop_local; - local_ref_count [cprop_local]++; - if (td->verbose_level) - dump_interp_inst (ins, td->data_items); - } else if (!local_defs [sreg].ins) { - td->locals [sreg].flags |= INTERP_LOCAL_FLAG_UNKNOWN_USE; - } -} - -static void -clear_local_defs (TransformData *td, int var, void *data) -{ - LocalValue *local_defs = (LocalValue*) data; - local_defs [var].type = LOCAL_VALUE_NONE; - local_defs [var].ins = NULL; - local_defs [var].ref_count = 0; -} - -static void -clear_unused_defs (TransformData *td, int var, void *data) -{ - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) - return; - if (td->locals [var].indirects) - return; - - LocalValue *local_def = &((LocalValue*) data) [var]; - InterpInst *def_ins = local_def->ins; - if (!def_ins) - return; - if (local_def->ref_count) - return; - - // This is a local only var that is defined in this bblock and its value is not used - // at all in this bblock. Clear the definition - if (MINT_NO_SIDE_EFFECTS (def_ins->opcode)) { - for (int i = 0; i < mono_interp_op_sregs [def_ins->opcode]; i++) - td->local_ref_count [def_ins->sregs [i]]--; - if (td->verbose_level) { - g_print ("kill unused local def:\n\t"); - dump_interp_inst (def_ins, td->data_items); - } - interp_clear_ins (def_ins); - } -} - -static void -interp_cprop (TransformData *td) -{ - LocalValue *local_defs = (LocalValue*) g_malloc (td->locals_size * sizeof (LocalValue)); - int *local_ref_count = (int*) g_malloc (td->locals_size * sizeof (int)); - InterpBasicBlock *bb; - gboolean needs_retry; - int ins_index; - int iteration_count = 0; - - td->local_ref_count = local_ref_count; -retry: - needs_retry = FALSE; - memset (local_ref_count, 0, td->locals_size * sizeof (int)); - - if (td->verbose_level) - g_print ("\ncprop iteration %d\n", iteration_count++); - - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - ins_index = 0; - - // Set cbb since we do some instruction inserting below - td->cbb = bb; - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) - foreach_local_var (td, ins, local_defs, clear_local_defs); - - if (td->verbose_level) { - GString* bb_info = get_interp_bb_links (bb); - g_print ("\nBB%d: %s\n", bb->index, bb_info->str); - g_string_free (bb_info, TRUE); - } - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - - if (opcode == MINT_NOP) - continue; - - int num_sregs = mono_interp_op_sregs [opcode]; - int num_dregs = mono_interp_op_dregs [opcode]; - gint32 *sregs = &ins->sregs [0]; - gint32 dreg = ins->dreg; - - if (td->verbose_level && ins->opcode != MINT_NOP && ins->opcode != MINT_IL_SEQ_POINT) - dump_interp_inst (ins, td->data_items); - - for (int i = 0; i < num_sregs; i++) { - if (sregs [i] == MINT_CALL_ARGS_SREG) { - if (ins->info.call_info && ins->info.call_info->call_args) { - int *call_args = ins->info.call_info->call_args; - while (*call_args != -1) { - cprop_sreg (td, ins, call_args, local_defs); - call_args++; - } - } - } else { - cprop_sreg (td, ins, &sregs [i], local_defs); - } - } - - if (num_dregs) { - // Check if the previous definition of this var was used at all. - // If it wasn't we can just clear the instruction - // - // MINT_MOV_DST_OFF doesn't fully write to the var, so we special case it here - if (local_defs [dreg].ins != NULL && - local_defs [dreg].ref_count == 0 && - !td->locals [dreg].indirects && - opcode != MINT_MOV_DST_OFF) { - InterpInst *prev_def = local_defs [dreg].ins; - if (MINT_NO_SIDE_EFFECTS (prev_def->opcode)) { - for (int i = 0; i < mono_interp_op_sregs [prev_def->opcode]; i++) - local_ref_count [prev_def->sregs [i]]--; - interp_clear_ins (prev_def); - } - } - local_defs [dreg].type = LOCAL_VALUE_NONE; - local_defs [dreg].ins = ins; - local_defs [dreg].def_index = ins_index; - } - - // We always store to the full i4, except as part of STIND opcodes. These opcodes can be - // applied to a local var only if that var has LDLOCA applied to it - if ((opcode >= MINT_MOV_I4_I1 && opcode <= MINT_MOV_I4_U2) && !td->locals [sregs [0]].indirects) { - ins->opcode = MINT_MOV_4; - opcode = MINT_MOV_4; - } - - if (opcode == MINT_MOV_4 || opcode == MINT_MOV_8 || opcode == MINT_MOV_VT) { - int sreg = sregs [0]; - if (dreg == sreg) { - if (td->verbose_level) - g_print ("clear redundant mov\n"); - interp_clear_ins (ins); - local_ref_count [sreg]--; - } else if (td->locals [sreg].indirects || td->locals [dreg].indirects) { - // Don't bother with indirect locals - } else if (local_defs [sreg].type == LOCAL_VALUE_I4 || local_defs [sreg].type == LOCAL_VALUE_I8) { - // Replace mov with ldc - gboolean is_i4 = local_defs [sreg].type == LOCAL_VALUE_I4; - g_assert (!td->locals [sreg].indirects); - local_defs [dreg].type = local_defs [sreg].type; - if (is_i4) { - int ct = local_defs [sreg].i; - ins = interp_get_ldc_i4_from_const (td, ins, ct, dreg); - local_defs [dreg].i = ct; - } else { - gint64 ct = local_defs [sreg].l; - ins = interp_inst_replace_with_i8_const (td, ins, ct); - local_defs [dreg].l = ct; - } - local_defs [dreg].ins = ins; - local_ref_count [sreg]--; - mono_interp_stats.copy_propagations++; - if (td->verbose_level) { - g_print ("cprop loc %d -> ct :\n\t", sreg); - dump_interp_inst (ins, td->data_items); - } - } else if (local_defs [sreg].ins != NULL && - (td->locals [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - !(td->locals [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - interp_prev_ins (ins) == local_defs [sreg].ins && - !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { - // hackish temporary optimization that won't be necessary in the future - // We replace `local1 <- ?, local2 <- local1` with `local2 <- ?, local1 <- local2` - // if local1 is execution stack local and local2 is normal global local. This makes - // it more likely for `local1 <- local2` to be killed, while before we always needed - // to store to the global local, which is likely accessed by other instructions. - InterpInst *def = local_defs [sreg].ins; - int original_dreg = def->dreg; - - def->dreg = dreg; - ins->dreg = original_dreg; - sregs [0] = dreg; - - local_defs [dreg].type = LOCAL_VALUE_NONE; - local_defs [dreg].ins = def; - local_defs [dreg].def_index = local_defs [original_dreg].def_index; - local_defs [dreg].ref_count++; - local_defs [original_dreg].type = LOCAL_VALUE_LOCAL; - local_defs [original_dreg].ins = ins; - local_defs [original_dreg].local = dreg; - local_defs [original_dreg].def_index = ins_index; - local_defs [original_dreg].ref_count--; - - local_ref_count [original_dreg]--; - local_ref_count [dreg]++; - - if (td->verbose_level) { - g_print ("cprop dreg:\n\t"); - dump_interp_inst (def, td->data_items); - g_print ("\t"); - dump_interp_inst (ins, td->data_items); - } - } else { - if (td->verbose_level) - g_print ("local copy %d <- %d\n", dreg, sreg); - local_defs [dreg].type = LOCAL_VALUE_LOCAL; - local_defs [dreg].local = sreg; - } - } else if (opcode == MINT_LDLOCA_S) { - // The local that we are taking the address of is not a sreg but still referenced - local_ref_count [ins->sregs [0]]++; - } else if (MINT_IS_LDC_I4 (opcode)) { - local_defs [dreg].type = LOCAL_VALUE_I4; - local_defs [dreg].i = interp_get_const_from_ldc_i4 (ins); - } else if (MINT_IS_LDC_I8 (opcode)) { - local_defs [dreg].type = LOCAL_VALUE_I8; - local_defs [dreg].l = interp_get_const_from_ldc_i8 (ins); - } else if (opcode == MINT_LDC_R4) { - guint32 val_u = READ32 (&ins->data [0]); - float f = *(float*)(&val_u); - local_defs [dreg].type = LOCAL_VALUE_R4; - local_defs [dreg].f = f; - } else if (ins->opcode == MINT_LDPTR) { -#if SIZEOF_VOID_P == 8 - local_defs [dreg].type = LOCAL_VALUE_I8; - local_defs [dreg].l = (gint64)td->data_items [ins->data [0]]; -#else - local_defs [dreg].type = LOCAL_VALUE_I4; - local_defs [dreg].i = (gint32)td->data_items [ins->data [0]]; -#endif - } else if (MINT_IS_UNOP (opcode)) { - ins = interp_fold_unop (td, local_defs, ins); - } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { - ins = interp_fold_unop_cond_br (td, bb, local_defs, ins); - } else if (MINT_IS_SIMD_CREATE (opcode)) { - ins = interp_fold_simd_create (td, bb, local_defs, ins); - } else if (MINT_IS_BINOP (opcode)) { - gboolean folded; - ins = interp_fold_binop (td, local_defs, ins, &folded); - if (!folded) { - int sreg = -1; - guint16 mov_op = 0; - if ((opcode == MINT_MUL_I4 || opcode == MINT_DIV_I4) && - local_defs [ins->sregs [1]].type == LOCAL_VALUE_I4 && - local_defs [ins->sregs [1]].i == 1) { - sreg = ins->sregs [0]; - mov_op = MINT_MOV_4; - } else if ((opcode == MINT_MUL_I8 || opcode == MINT_DIV_I8) && - local_defs [ins->sregs [1]].type == LOCAL_VALUE_I8 && - local_defs [ins->sregs [1]].l == 1) { - sreg = ins->sregs [0]; - mov_op = MINT_MOV_8; - } else if (opcode == MINT_MUL_I4 && - local_defs [ins->sregs [0]].type == LOCAL_VALUE_I4 && - local_defs [ins->sregs [0]].i == 1) { - sreg = ins->sregs [1]; - mov_op = MINT_MOV_4; - } else if (opcode == MINT_MUL_I8 && - local_defs [ins->sregs [0]].type == LOCAL_VALUE_I8 && - local_defs [ins->sregs [0]].l == 1) { - sreg = ins->sregs [1]; - mov_op = MINT_MOV_8; - } - if (sreg != -1) { - ins->opcode = mov_op; - ins->sregs [0] = sreg; - if (td->verbose_level) { - g_print ("Replace idempotent binop :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } - } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { - ins = interp_fold_binop_cond_br (td, bb, local_defs, ins); - } else if (MINT_IS_LDIND (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int local = ldloca->sregs [0]; - int mt = td->locals [local].mt; - if (mt != MINT_TYPE_VT) { - // LDIND cannot simply be replaced with a mov because it might also include a - // necessary conversion (especially when we do cprop and do moves between vars of - // different types). - int ldind_mt = interp_get_mt_for_ldind (opcode); - switch (ldind_mt) { - case MINT_TYPE_I1: ins->opcode = MINT_CONV_I1_I4; break; - case MINT_TYPE_U1: ins->opcode = MINT_CONV_U1_I4; break; - case MINT_TYPE_I2: ins->opcode = MINT_CONV_I2_I4; break; - case MINT_TYPE_U2: ins->opcode = MINT_CONV_U2_I4; break; - default: - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (ldind_mt, FALSE)); - break; - } - local_ref_count [sregs [0]]--; - interp_ins_set_sreg (ins, local); - - if (td->verbose_level) { - g_print ("Replace ldloca/ldind pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } - } else if (MINT_IS_LDFLD (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int mt = ins->opcode - MINT_LDFLD_I1; - int local = ldloca->sregs [0]; - // Allow ldloca instruction to be killed - local_ref_count [sregs [0]]--; - if (td->locals [local].mt == (ins->opcode - MINT_LDFLD_I1) && ins->data [0] == 0) { - // Replace LDLOCA + LDFLD with LDLOC, when the loading field represents - // the entire local. This is the case with loading the only field of an - // IntPtr. We don't handle value type loads. - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, TRUE)); - // The dreg of the MOV is the same as the dreg of the LDFLD - sregs [0] = local; - } else { - // Add mov.src.off to load directly from the local var space without use of ldloca. - int foffset = ins->data [0]; - guint16 ldsize = 0; - if (mt == MINT_TYPE_VT) - ldsize = ins->data [1]; - - // This loads just a part of the local valuetype - ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); - interp_ins_set_dreg (ins, ins->prev->dreg); - interp_ins_set_sreg (ins, local); - ins->data [0] = GINT_TO_UINT16 (foffset); - ins->data [1] = GINT_TO_UINT16 (mt); - if (mt == MINT_TYPE_VT) - ins->data [2] = ldsize; - - interp_clear_ins (ins->prev); - } - - if (td->verbose_level) { - g_print ("Replace ldloca/ldfld pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } else if (opcode == MINT_INITOBJ) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int size = ins->data [0]; - int local = ldloca->sregs [0]; - // Replace LDLOCA + INITOBJ with or LDC - if (size <= 4) - ins->opcode = MINT_LDC_I4_0; - else if (size <= 8) - ins->opcode = MINT_LDC_I8_0; - else - ins->opcode = MINT_INITLOCAL; - local_ref_count [sregs [0]]--; - ins->dreg = local; - - if (td->verbose_level) { - g_print ("Replace ldloca/initobj pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } else if (opcode == MINT_LDOBJ_VT) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int ldsize = ins->data [0]; - int local = ldloca->sregs [0]; - local_ref_count [sregs [0]]--; - - if (ldsize == td->locals [local].size) { - // Replace LDLOCA + LDOBJ_VT with MOV_VT - ins->opcode = MINT_MOV_VT; - sregs [0] = local; - needs_retry = TRUE; - } else { - // This loads just a part of the local valuetype - ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); - interp_ins_set_dreg (ins, ins->prev->dreg); - interp_ins_set_sreg (ins, local); - ins->data [0] = 0; - ins->data [1] = MINT_TYPE_VT; - ins->data [2] = GINT_TO_UINT16 (ldsize); - - interp_clear_ins (ins->prev); - } - if (td->verbose_level) { - g_print ("Replace ldloca/ldobj_vt pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - } - } else if (opcode == MINT_STOBJ_VT || opcode == MINT_STOBJ_VT_NOREF) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int stsize = ins->data [0]; - int local = ldloca->sregs [0]; - - if (stsize == td->locals [local].size) { - // Replace LDLOCA + STOBJ_VT with MOV_VT - local_ref_count [sregs [0]]--; - ins->opcode = MINT_MOV_VT; - sregs [0] = sregs [1]; - ins->dreg = local; - needs_retry = TRUE; - - if (td->verbose_level) { - g_print ("Replace ldloca/stobj_vt pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - } - } - } else if (MINT_IS_STIND (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int local = ldloca->sregs [0]; - int mt = td->locals [local].mt; - if (mt != MINT_TYPE_VT) { - // We have an 8 byte local, just replace the stind with a mov - local_ref_count [sregs [0]]--; - // We make the assumption that the STIND matches the local type - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, TRUE)); - interp_ins_set_dreg (ins, local); - interp_ins_set_sreg (ins, sregs [1]); - - if (td->verbose_level) { - g_print ("Replace ldloca/stind pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } - } else if (MINT_IS_STFLD (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int mt = ins->opcode - MINT_STFLD_I1; - int local = ldloca->sregs [0]; - local_ref_count [sregs [0]]--; - // Allow ldloca instruction to be killed - if (td->locals [local].mt == (ins->opcode - MINT_STFLD_I1) && ins->data [0] == 0) { - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, FALSE)); - // The sreg of the MOV is the same as the second sreg of the STFLD - ins->dreg = local; - sregs [0] = sregs [1]; - } else { - // Add mov.dst.off to store directly int the local var space without use of ldloca. - int foffset = ins->data [0]; - guint16 vtsize = 0; - if (mt == MINT_TYPE_VT) { - vtsize = ins->data [1]; - } -#ifdef NO_UNALIGNED_ACCESS - else { - // As with normal loads/stores we use memcpy for unaligned 8 byte accesses - if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && foffset % SIZEOF_VOID_P != 0) - vtsize = 8; - } -#endif - - // This stores just to part of the dest valuetype - ins = interp_insert_ins (td, ins, MINT_MOV_DST_OFF); - interp_ins_set_dreg (ins, local); - interp_ins_set_sreg (ins, sregs [1]); - ins->data [0] = GINT_TO_UINT16 (foffset); - ins->data [1] = GINT_TO_UINT16 (mt); - ins->data [2] = vtsize; - - interp_clear_ins (ins->prev); - } - if (td->verbose_level) { - g_print ("Replace ldloca/stfld pair (off %p) :\n\t", (void *)(uintptr_t) ldloca->il_offset); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } else if (opcode == MINT_GETITEM_SPAN) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int local = ldloca->sregs [0]; - // Allow ldloca instruction to be killed - local_ref_count [sregs [0]]--; - // Instead of loading from the indirect pointer pass directly the vt var - ins->opcode = MINT_GETITEM_LOCALSPAN; - sregs [0] = local; - needs_retry = TRUE; - } - } else if (opcode == MINT_CKNULL) { - InterpInst *def = local_defs [sregs [0]].ins; - if (def && def->opcode == MINT_LDLOCA_S) { - // CKNULL on LDLOCA is a NOP - ins->opcode = MINT_MOV_P; - needs_retry = TRUE; - } - } else if (opcode == MINT_BOX) { - // TODO Add more relevant opcodes - local_defs [dreg].type = LOCAL_VALUE_NON_NULL; - } - - ins_index++; - } - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) - foreach_local_var (td, ins, local_defs, clear_unused_defs); - } - - needs_retry |= interp_local_deadce (td); - if (mono_interp_opt & INTERP_OPT_BBLOCKS) - needs_retry |= interp_optimize_bblocks (td); - - if (needs_retry) - goto retry; - - g_free (local_defs); -} - -void -mono_test_interp_cprop (TransformData *td) -{ - interp_cprop (td); -} - -static gboolean -get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) -{ - InterpInst *def = td->locals [sreg].def; - if (def != NULL && td->local_ref_count [sreg] == 1) { - gint64 ct; - if (MINT_IS_LDC_I4 (def->opcode)) - ct = interp_get_const_from_ldc_i4 (def); - else if (MINT_IS_LDC_I8 (def->opcode)) - ct = interp_get_const_from_ldc_i8 (def); - else - return FALSE; - gint64 min_val, max_val; - // We only propagate the immediate only if it fits into the desired type, - // so we don't accidentaly handle conversions wrong - switch (result_mt) { - case MINT_TYPE_I1: - min_val = G_MININT8; - max_val = G_MAXINT8; - break; - case MINT_TYPE_I2: - min_val = G_MININT16; - max_val = G_MAXINT16; - break; - case MINT_TYPE_U1: - min_val = 0; - max_val = G_MAXUINT8; - break; - case MINT_TYPE_U2: - min_val = 0; - max_val = G_MAXINT16; - break; - default: - g_assert_not_reached (); - - } - if (ct >= min_val && ct <= max_val) { - *imm = (gint16)ct; - mono_interp_stats.super_instructions++; - return TRUE; - } - } - return FALSE; -} - -static int -get_binop_condbr_imm_sp (int opcode) -{ - switch (opcode) { - case MINT_BEQ_I4: return MINT_BEQ_I4_IMM_SP; - case MINT_BEQ_I8: return MINT_BEQ_I8_IMM_SP; - case MINT_BGE_I4: return MINT_BGE_I4_IMM_SP; - case MINT_BGE_I8: return MINT_BGE_I8_IMM_SP; - case MINT_BGT_I4: return MINT_BGT_I4_IMM_SP; - case MINT_BGT_I8: return MINT_BGT_I8_IMM_SP; - case MINT_BLT_I4: return MINT_BLT_I4_IMM_SP; - case MINT_BLT_I8: return MINT_BLT_I8_IMM_SP; - case MINT_BLE_I4: return MINT_BLE_I4_IMM_SP; - case MINT_BLE_I8: return MINT_BLE_I8_IMM_SP; - case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_IMM_SP; - case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_IMM_SP; - case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_IMM_SP; - case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_IMM_SP; - case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_IMM_SP; - case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_IMM_SP; - case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_IMM_SP; - case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_IMM_SP; - case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_IMM_SP; - case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_IMM_SP; - default: return MINT_NOP; - } -} - -static int -get_binop_condbr_sp (int opcode) -{ - switch (opcode) { - case MINT_BEQ_I4: return MINT_BEQ_I4_SP; - case MINT_BEQ_I8: return MINT_BEQ_I8_SP; - case MINT_BGE_I4: return MINT_BGE_I4_SP; - case MINT_BGE_I8: return MINT_BGE_I8_SP; - case MINT_BGT_I4: return MINT_BGT_I4_SP; - case MINT_BGT_I8: return MINT_BGT_I8_SP; - case MINT_BLT_I4: return MINT_BLT_I4_SP; - case MINT_BLT_I8: return MINT_BLT_I8_SP; - case MINT_BLE_I4: return MINT_BLE_I4_SP; - case MINT_BLE_I8: return MINT_BLE_I8_SP; - case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_SP; - case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_SP; - case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_SP; - case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_SP; - case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_SP; - case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_SP; - case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_SP; - case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_SP; - case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_SP; - case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_SP; - default: return MINT_NOP; - } -} - -static int -get_unop_condbr_sp (int opcode) -{ - switch (opcode) { - case MINT_BRFALSE_I4: return MINT_BRFALSE_I4_SP; - case MINT_BRFALSE_I8: return MINT_BRFALSE_I8_SP; - case MINT_BRTRUE_I4: return MINT_BRTRUE_I4_SP; - case MINT_BRTRUE_I8: return MINT_BRTRUE_I8_SP; - default: return MINT_NOP; - } -} - -static void -interp_super_instructions (TransformData *td) -{ - InterpBasicBlock *bb; - int *local_ref_count = td->local_ref_count; - - compute_native_offset_estimates (td); - - // Add some actual super instructions - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - int noe; - - // Set cbb since we do some instruction inserting below - td->cbb = bb; - noe = bb->native_offset_estimate; - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - if (MINT_IS_NOP (opcode)) - continue; - if (mono_interp_op_dregs [opcode] && !(td->locals [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) - td->locals [ins->dreg].def = ins; - - if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { - // ldc + ret -> ret.imm - int sreg = ins->sregs [0]; - gint16 imm; - if (get_sreg_imm (td, sreg, &imm, (opcode == MINT_RET) ? MINT_TYPE_I2 : opcode - MINT_RET_I1)) { - InterpInst *def = td->locals [sreg].def; - int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; - InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); - new_inst->data [0] = imm; - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg]--; - - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8 || - opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8) { - int sreg = -1; - int sreg_imm = -1; - gint16 imm; - if (get_sreg_imm (td, ins->sregs [0], &imm, MINT_TYPE_I2)) { - sreg = ins->sregs [1]; - sreg_imm = ins->sregs [0]; - } else if (get_sreg_imm (td, ins->sregs [1], &imm, MINT_TYPE_I2)) { - sreg = ins->sregs [0]; - sreg_imm = ins->sregs [1]; - } - if (sreg != -1) { - int binop; - switch (opcode) { - case MINT_ADD_I4: binop = MINT_ADD_I4_IMM; break; - case MINT_ADD_I8: binop = MINT_ADD_I8_IMM; break; - case MINT_MUL_I4: binop = MINT_MUL_I4_IMM; break; - case MINT_MUL_I8: binop = MINT_MUL_I8_IMM; break; - default: g_assert_not_reached (); - } - InterpInst *new_inst = interp_insert_ins (td, ins, binop); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = sreg; - new_inst->data [0] = imm; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } else if (opcode == MINT_SUB_I4 || opcode == MINT_SUB_I8) { - // ldc + sub -> add.-imm - gint16 imm; - int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2) && imm != G_MININT16) { - int add_op = opcode == MINT_SUB_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; - InterpInst *new_inst = interp_insert_ins (td, ins, add_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = -imm; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } else if (opcode == MINT_MUL_I4_IMM || opcode == MINT_MUL_I8_IMM) { - int sreg = ins->sregs [0]; - InterpInst *def = td->locals [sreg].def; - if (def != NULL && td->local_ref_count [sreg] == 1) { - gboolean is_i4 = opcode == MINT_MUL_I4_IMM; - if ((is_i4 && def->opcode == MINT_ADD_I4_IMM) || - (!is_i4 && def->opcode == MINT_ADD_I8_IMM)) { - InterpInst *new_inst = interp_insert_ins (td, ins, is_i4 ? MINT_ADD_MUL_I4_IMM : MINT_ADD_MUL_I8_IMM); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = def->sregs [0]; - new_inst->data [0] = def->data [0]; - new_inst->data [1] = ins->data [0]; - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_BINOP_SHIFT (opcode)) { - gint16 imm; - int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { - // ldc + sh -> sh.imm - int shift_op = MINT_SHR_UN_I4_IMM + (opcode - MINT_SHR_UN_I4); - InterpInst *new_inst = interp_insert_ins (td, ins, shift_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = imm; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } else if (opcode == MINT_SHL_I4 || opcode == MINT_SHL_I8) { - int amount_var = ins->sregs [1]; - InterpInst *amount_def = td->locals [amount_var].def; - if (amount_def != NULL && td->local_ref_count [amount_var] == 1 && amount_def->opcode == MINT_AND_I4) { - int mask_var = amount_def->sregs [1]; - if (get_sreg_imm (td, mask_var, &imm, MINT_TYPE_I2)) { - // ldc + and + shl -> shl_and_imm - int new_opcode = -1; - if (opcode == MINT_SHL_I4 && imm == 31) - new_opcode = MINT_SHL_AND_I4; - else if (opcode == MINT_SHL_I8 && imm == 63) - new_opcode = MINT_SHL_AND_I8; - - if (new_opcode != -1) { - InterpInst *new_inst = interp_insert_ins (td, ins, new_opcode); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->sregs [1] = amount_def->sregs [0]; - - local_ref_count [amount_var]--; - local_ref_count [mask_var]--; - - interp_clear_ins (td->locals [mask_var].def); - interp_clear_ins (amount_def); - interp_clear_ins (ins); - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } - } - } else if (opcode == MINT_DIV_UN_I4 || opcode == MINT_DIV_UN_I8) { - // ldc + div.un -> shr.imm - int sreg_imm = ins->sregs [1]; - InterpInst *def = td->locals [sreg_imm].def; - if (def != NULL && td->local_ref_count [sreg_imm] == 1) { - int power2 = -1; - if (MINT_IS_LDC_I4 (def->opcode)) { - guint32 ct = interp_get_const_from_ldc_i4 (def); - power2 = mono_is_power_of_two ((guint32)ct); - } else if (MINT_IS_LDC_I8 (def->opcode)) { - guint64 ct = interp_get_const_from_ldc_i8 (def); - if (ct < G_MAXUINT32) - power2 = mono_is_power_of_two ((guint32)ct); - } - if (power2 > 0) { - InterpInst *new_inst; - if (opcode == MINT_DIV_UN_I4) - new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I4_IMM); - else - new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I8_IMM); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = GINT_TO_UINT16 (power2); - - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("lower div.un: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_LDIND_INT (opcode)) { - int sreg_base = ins->sregs [0]; - InterpInst *def = td->locals [sreg_base].def; - if (def != NULL && td->local_ref_count [sreg_base] == 1) { - InterpInst *new_inst = NULL; - if (def->opcode == MINT_ADD_P) { - int ldind_offset_op = MINT_LDIND_OFFSET_I1 + (opcode - MINT_LDIND_I1); - new_inst = interp_insert_ins (td, ins, ldind_offset_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->sregs [1] = def->sregs [1]; // off - } else if (def->opcode == MINT_ADD_P_IMM) { - int ldind_offset_imm_op = MINT_LDIND_OFFSET_IMM_I1 + (opcode - MINT_LDIND_I1); - new_inst = interp_insert_ins (td, ins, ldind_offset_imm_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->data [0] = def->data [0]; // imm value - } - if (new_inst) { - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_base]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_LDIND_OFFSET (opcode)) { - int sreg_off = ins->sregs [1]; - InterpInst *def = td->locals [sreg_off].def; - if (def != NULL && td->local_ref_count [sreg_off] == 1) { - if (def->opcode == MINT_MUL_P_IMM || def->opcode == MINT_ADD_P_IMM || def->opcode == MINT_ADD_MUL_P_IMM) { - int ldind_offset_op = MINT_LDIND_OFFSET_ADD_MUL_IMM_I1 + (opcode - MINT_LDIND_OFFSET_I1); - InterpInst *new_inst = interp_insert_ins (td, ins, ldind_offset_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; // base - new_inst->sregs [1] = def->sregs [0]; // off - - // set the add and mul immediates - switch (def->opcode) { - case MINT_ADD_P_IMM: - new_inst->data [0] = def->data [0]; - new_inst->data [1] = 1; - break; - case MINT_MUL_P_IMM: - new_inst->data [0] = 0; - new_inst->data [1] = def->data [0]; - break; - case MINT_ADD_MUL_P_IMM: - new_inst->data [0] = def->data [0]; - new_inst->data [1] = def->data [1]; - break; - } - - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_off]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("method %s:%s, superins: ", m_class_get_name (td->method->klass), td->method->name); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_STIND_INT (opcode)) { - int sreg_base = ins->sregs [0]; - InterpInst *def = td->locals [sreg_base].def; - if (def != NULL && td->local_ref_count [sreg_base] == 1) { - InterpInst *new_inst = NULL; - if (def->opcode == MINT_ADD_P) { - int stind_offset_op = MINT_STIND_OFFSET_I1 + (opcode - MINT_STIND_I1); - new_inst = interp_insert_ins (td, ins, stind_offset_op); - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->sregs [1] = def->sregs [1]; // off - new_inst->sregs [2] = ins->sregs [1]; // value - } else if (def->opcode == MINT_ADD_P_IMM) { - int stind_offset_imm_op = MINT_STIND_OFFSET_IMM_I1 + (opcode - MINT_STIND_I1); - new_inst = interp_insert_ins (td, ins, stind_offset_imm_op); - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->sregs [1] = ins->sregs [1]; // value - new_inst->data [0] = def->data [0]; // imm value - } - if (new_inst) { - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_base]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_LDFLD (opcode)) { - // cknull + ldfld -> ldfld - // FIXME This optimization is very limited, it is meant mainly to remove cknull - // when inlining property accessors. We should have more advanced cknull removal - // optimzations, so we can catch cases where instructions are not next to each other. - int obj_sreg = ins->sregs [0]; - InterpInst *def = td->locals [obj_sreg].def; - if (def != NULL && def->opcode == MINT_CKNULL && interp_prev_ins (ins) == def && - def->dreg == obj_sreg && local_ref_count [obj_sreg] == 1) { - if (td->verbose_level) { - g_print ("remove redundant cknull (%s): ", td->method->name); - dump_interp_inst (def, td->data_items); - } - ins->sregs [0] = def->sregs [0]; - interp_clear_ins (def); - local_ref_count [obj_sreg]--; - mono_interp_stats.super_instructions++; - } - } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { - gint16 imm; - int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { - int condbr_op = get_binop_condbr_imm_sp (opcode); - if (condbr_op != MINT_NOP) { - InterpInst *prev_ins = interp_prev_ins (ins); - // The new instruction does a safepoint - if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) - interp_clear_ins (prev_ins); - InterpInst *new_ins = interp_insert_ins (td, ins, condbr_op); - new_ins->sregs [0] = ins->sregs [0]; - new_ins->data [0] = imm; - new_ins->info.target_bb = ins->info.target_bb; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_ins, td->data_items); - } - } - } else { - InterpInst *prev_ins = interp_prev_ins (ins); - if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { - int condbr_op = get_binop_condbr_sp (opcode); - if (condbr_op != MINT_NOP) { - interp_clear_ins (prev_ins); - ins->opcode = GINT_TO_OPCODE (condbr_op); - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (ins, td->data_items); - } - } - } - } - } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode) && is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { - if (opcode == MINT_BRFALSE_I4 || opcode == MINT_BRTRUE_I4) { - gboolean negate = opcode == MINT_BRFALSE_I4; - int cond_sreg = ins->sregs [0]; - InterpInst *def = td->locals [cond_sreg].def; - if (def != NULL && local_ref_count [cond_sreg] == 1) { - int replace_opcode = -1; - switch (def->opcode) { - case MINT_CEQ_I4: replace_opcode = negate ? MINT_BNE_UN_I4 : MINT_BEQ_I4; break; - case MINT_CEQ_I8: replace_opcode = negate ? MINT_BNE_UN_I8 : MINT_BEQ_I8; break; - case MINT_CGT_I4: replace_opcode = negate ? MINT_BLE_I4 : MINT_BGT_I4; break; - case MINT_CGT_I8: replace_opcode = negate ? MINT_BLE_I8 : MINT_BGT_I8; break; - case MINT_CLT_I4: replace_opcode = negate ? MINT_BGE_I4 : MINT_BLT_I4; break; - case MINT_CLT_I8: replace_opcode = negate ? MINT_BGE_I8 : MINT_BLT_I8; break; - case MINT_CGT_UN_I4: replace_opcode = negate ? MINT_BLE_UN_I4 : MINT_BGT_UN_I4; break; - case MINT_CGT_UN_I8: replace_opcode = negate ? MINT_BLE_UN_I8 : MINT_BGT_UN_I8; break; - case MINT_CLT_UN_I4: replace_opcode = negate ? MINT_BGE_UN_I4 : MINT_BLT_UN_I4; break; - case MINT_CLT_UN_I8: replace_opcode = negate ? MINT_BGE_UN_I8 : MINT_BLT_UN_I8; break; - case MINT_CEQ_R4: replace_opcode = negate ? MINT_BNE_UN_R4 : MINT_BEQ_R4; break; - case MINT_CEQ_R8: replace_opcode = negate ? MINT_BNE_UN_R8 : MINT_BEQ_R8; break; - case MINT_CGT_R4: replace_opcode = negate ? MINT_BLE_UN_R4 : MINT_BGT_R4; break; - case MINT_CGT_R8: replace_opcode = negate ? MINT_BLE_UN_R8 : MINT_BGT_R8; break; - case MINT_CLT_R4: replace_opcode = negate ? MINT_BGE_UN_R4 : MINT_BLT_R4; break; - case MINT_CLT_R8: replace_opcode = negate ? MINT_BGE_UN_R8 : MINT_BLT_R8; break; - case MINT_CGT_UN_R4: replace_opcode = negate ? MINT_BLE_R4 : MINT_BGT_UN_R4; break; - case MINT_CGT_UN_R8: replace_opcode = negate ? MINT_BLE_R8 : MINT_BGT_UN_R8; break; - case MINT_CLT_UN_R4: replace_opcode = negate ? MINT_BGE_R4 : MINT_BLT_UN_R4; break; - case MINT_CLT_UN_R8: replace_opcode = negate ? MINT_BGE_R8 : MINT_BLT_UN_R8; break; - case MINT_CEQ0_I4: replace_opcode = negate ? MINT_BRTRUE_I4 : MINT_BRFALSE_I4; break; // If def->opcode is MINT_CEQ0_I4 ins->opcode is inverted - // Add more opcodes - default: - break; - } - if (replace_opcode != -1) { - ins->opcode = GINT_TO_UINT16 (replace_opcode); - ins->sregs [0] = def->sregs [0]; - if (def->opcode != MINT_CEQ0_I4) - ins->sregs [1] = def->sregs [1]; - interp_clear_ins (def); - local_ref_count [cond_sreg]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (ins, td->data_items); - } - // The newly added opcode could be part of further superinstructions. Retry - ins = ins->prev; - continue; - } - } - } - InterpInst *prev_ins = interp_prev_ins (ins); - if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { - int condbr_op = get_unop_condbr_sp (opcode); - if (condbr_op != MINT_NOP) { - interp_clear_ins (prev_ins); - ins->opcode = GINT_TO_OPCODE (condbr_op); - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (ins, td->data_items); - } - } - } - } else if (opcode == MINT_STOBJ_VT_NOREF) { - int sreg_src = ins->sregs [1]; - InterpInst *def = td->locals [sreg_src].def; - if (def != NULL && interp_prev_ins (ins) == def && def->opcode == MINT_LDOBJ_VT && ins->data [0] == def->data [0] && td->local_ref_count [sreg_src] == 1) { - InterpInst *new_inst = interp_insert_ins (td, ins, MINT_CPOBJ_VT_NOREF); - new_inst->sregs [0] = ins->sregs [0]; // dst - new_inst->sregs [1] = def->sregs [0]; // src - new_inst->data [0] = ins->data [0]; // size - - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_src]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - noe += get_inst_length (ins); - } - } -} - -static void initialize_global_vars (TransformData *td); - -static void -interp_optimize_code (TransformData *td) -{ - if (mono_interp_opt & INTERP_OPT_BBLOCKS) - interp_optimize_bblocks (td); - - if (mono_interp_opt & INTERP_OPT_CPROP) - MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); - - // After this point control optimizations on control flow can no longer happen, so we can determine - // which vars are global. This helps speed up the super instructions pass, which only operates on - // single def, single use local vars. - initialize_global_vars (td); - - if ((mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) && - (mono_interp_opt & INTERP_OPT_CPROP)) - MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); -} - -static void -set_var_live_range (TransformData *td, int var, int ins_index) -{ - // We don't track liveness yet for global vars - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) - return; - if (td->locals [var].live_start == -1) - td->locals [var].live_start = ins_index; - td->locals [var].live_end = ins_index; -} - -static void -set_var_live_range_cb (TransformData *td, int var, gpointer data) -{ - set_var_live_range (td, var, (int)(gsize)data); -} - -static void -initialize_global_var (TransformData *td, int var, int bb_index) -{ - // Check if already handled - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) - return; - - if (td->locals [var].bb_index == -1) { - td->locals [var].bb_index = bb_index; - } else if (td->locals [var].bb_index != bb_index) { - // var used in multiple basic blocks - if (td->verbose_level) - g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); - alloc_global_var_offset (td, var); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; - } -} - -static void -initialize_global_var_cb (TransformData *td, int var, gpointer data) -{ - initialize_global_var (td, var, (int)(gsize)data); -} - -static void -initialize_global_vars (TransformData *td) -{ - InterpBasicBlock *bb; - - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - if (opcode == MINT_NOP) { - continue; - } else if (opcode == MINT_LDLOCA_S) { - int var = ins->sregs [0]; - // If global flag is set, it means its offset was already allocated - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { - if (td->verbose_level) - g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); - alloc_global_var_offset (td, var); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; - } - } - foreach_local_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); - } - } - td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT); -} - -// Data structure used for offset allocation of call args -typedef struct { - InterpInst **active_calls; - int active_calls_count; - int active_calls_capacity; - // A deferred call stack implemented as a linked list - GSList *deferred_calls; -} ActiveCalls; - -static void -init_active_calls (TransformData *td, ActiveCalls *ac) -{ - ac->active_calls_count = 0; - ac->active_calls_capacity = 5; - ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); - ac->deferred_calls = NULL; -} - -static void -reinit_active_calls (TransformData *td, ActiveCalls *ac) -{ - ac->active_calls_count = 0; - ac->deferred_calls = NULL; -} - -static void -add_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) -{ - // Check if already added - if (call->flags & INTERP_INST_FLAG_ACTIVE_CALL) - return; - - if (ac->active_calls_count == ac->active_calls_capacity) { - InterpInst **old = ac->active_calls; - ac->active_calls_capacity *= 2; - ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); - memcpy (ac->active_calls, old, ac->active_calls_count * sizeof (InterpInst*)); - } - ac->active_calls [ac->active_calls_count] = call; - ac->active_calls_count++; - - // Mark a flag on it so we don't have to lookup the array with every argument store. - call->flags |= INTERP_INST_FLAG_ACTIVE_CALL; -} - -/** - * Function allocates offsets of resolved calls following a constraint - * where the base offset of a call must be greater than the offset of any argument of other active call args. - * - * Function first removes the call from an array of active calls. If a match is found, - * the call is removed from the array by moving the last entry into its place. Otherwise, it is a call without arguments. - * - * If there are active calls, the call in question is push onto the stack as a deferred call. - * The call contains a list of other active calls on which it depends. Those calls need to be resolved first in order to determine optimal base offset for the call in question. - * Otherwise, if there are no active calls, function starts resolving the call in question and deferred calls from the stack. - * - * For each call, function computes the base offset, the offset of each call argument starting from a base offset, and stores the computed call offset into a InterpInst. - * The base offset is computed as max offset of all call offsets on which the call depends. - * Stack ensures that all call offsets on which the call depends are calculated before the call in question, by deferring calls from the last to the first one. - */ -static void -end_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) -{ - // Remove call from array - for (int i = 0; i < ac->active_calls_count; i++) { - if (ac->active_calls [i] == call) { - ac->active_calls_count--; - // Since this entry is removed, move the last entry into it - if (ac->active_calls_count > 0 && i < ac->active_calls_count) - ac->active_calls [i] = ac->active_calls [ac->active_calls_count]; - break; - } - } - - // Push active call that should be resolved onto the stack - call->info.call_info->call_deps = NULL; - if (ac->active_calls_count) { - for (int i = 0; i < ac->active_calls_count; i++) - call->info.call_info->call_deps = g_slist_prepend_mempool (td->mempool, call->info.call_info->call_deps, ac->active_calls [i]); - ac->deferred_calls = g_slist_prepend_mempool (td->mempool, ac->deferred_calls, call); - } else { - // If no other active calls, current active call and all deferred calls can be resolved from the stack - InterpInst *deferred_call = call; - while (deferred_call) { - // `base_offset` is a relative offset (to the start of the call args stack) where the args for this call reside. - // The deps for a call represent the list of active calls at the moment when the call ends. This means that all deps for a call end after the call in question. - // Given we iterate over the list of deferred calls from the last to the first one to end, all deps of a call are guaranteed to have been processed at this point. - int base_offset = 0; - for (GSList *list = deferred_call->info.call_info->call_deps; list; list = list->next) { - int end_offset = ((InterpInst*)list->data)->info.call_info->call_end_offset; - if (end_offset > base_offset) - base_offset = end_offset; - } - deferred_call->info.call_info->call_offset = base_offset; - // Compute to offset of each call argument - int *call_args = deferred_call->info.call_info->call_args; - if (call_args && (*call_args != -1)) { - int var = *call_args; - while (var != -1) { - alloc_var_offset (td, var, &base_offset); - call_args++; - var = *call_args; - } - } - deferred_call->info.call_info->call_end_offset = ALIGN_TO (base_offset, MINT_STACK_ALIGNMENT); - - if (ac->deferred_calls) { - deferred_call = (InterpInst*) ac->deferred_calls->data; - ac->deferred_calls = ac->deferred_calls->next; - } else - deferred_call = NULL; - } - } -} - -// Data structure used for offset allocation of local vars - -typedef struct { - int var; - gboolean is_alive; -} ActiveVar; - -typedef struct { - ActiveVar *active_vars; - int active_vars_count; - int active_vars_capacity; -} ActiveVars; - -static void -init_active_vars (TransformData *td, ActiveVars *av) -{ - av->active_vars_count = 0; - av->active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); - av->active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVars)); -} - -static void -reinit_active_vars (TransformData *td, ActiveVars *av) -{ - av->active_vars_count = 0; -} - -static void -add_active_var (TransformData *td, ActiveVars *av, int var) -{ - if (av->active_vars_count == av->active_vars_capacity) { - av->active_vars_capacity *= 2; - ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVar)); - memcpy (new_array, av->active_vars, av->active_vars_count * sizeof (ActiveVar)); - av->active_vars = new_array; - } - av->active_vars [av->active_vars_count].var = var; - av->active_vars [av->active_vars_count].is_alive = TRUE; - av->active_vars_count++; -} - -static void -end_active_var (TransformData *td, ActiveVars *av, int var) -{ - // Iterate over active vars, set the entry associated with var as !is_alive - for (int i = 0; i < av->active_vars_count; i++) { - if (av->active_vars [i].var == var) { - av->active_vars [i].is_alive = FALSE; - return; - } - } -} - -static void -compact_active_vars (TransformData *td, ActiveVars *av, gint32 *current_offset) -{ - if (!av->active_vars_count) - return; - int i = av->active_vars_count - 1; - while (i >= 0 && !av->active_vars [i].is_alive) { - av->active_vars_count--; - *current_offset = td->locals [av->active_vars [i].var].offset; - i--; - } -} - -static void -dump_active_vars (TransformData *td, ActiveVars *av) -{ - if (td->verbose_level) { - g_print ("active :"); - for (int i = 0; i < av->active_vars_count; i++) { - if (av->active_vars [i].is_alive) - g_print (" %d (end %d),", av->active_vars [i].var, td->locals [av->active_vars [i].var].live_end); - } - g_print ("\n"); - } -} - -static void -interp_alloc_offsets (TransformData *td) -{ - InterpBasicBlock *bb; - ActiveCalls ac; - ActiveVars av; - - if (td->verbose_level) - g_print ("\nvar offset allocator iteration\n"); - - initialize_global_vars (td); - - init_active_vars (td, &av); - init_active_calls (td, &ac); - - int final_total_locals_size = td->total_locals_size; - // We now have the top of stack offset. All local regs are allocated after this offset, with each basic block - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - int ins_index = 0; - if (td->verbose_level) - g_print ("BB%d\n", bb->index); - - reinit_active_calls (td, &ac); - reinit_active_vars (td, &av); - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - if (ins->opcode == MINT_NOP) - continue; - if (ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_VT || - ins->opcode == MINT_NEWOBJ_SLOW || ins->opcode == MINT_NEWOBJ_STRING) { - // The offset allocator assumes that the liveness of destination var starts - // after the source vars, which means the destination var can be allocated - // at the same offset as some of the arguments. However, for newobj opcodes, - // the created object is set before the call is made. We solve this by making - // sure that the dreg is not allocated in the param area, so there is no - // risk of conflicts. - td->locals [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; - } - if (ins->flags & INTERP_INST_FLAG_CALL) { - if (ins->info.call_info && ins->info.call_info->call_args) { - int *call_args = ins->info.call_info->call_args; - guint16 pair_sregs [MINT_MOV_PAIRS_MAX]; - guint16 pair_dregs [MINT_MOV_PAIRS_MAX]; - int num_pairs = 0; - int var = *call_args; - - while (var != -1) { - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL || - !td->local_ref_count || td->local_ref_count [var] > 1 || - td->locals [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { - // Some vars can't be allocated on the call args stack, since the constraint is that - // call args vars die after the call. This isn't necessarily true for global vars or - // vars that are used by other instructions aside from the call. - // We need to copy the var into a new tmp var - int new_var = create_interp_local (td, td->locals [var].type); - td->locals [new_var].call = ins; - td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - - int mt = mono_mint_type (td->locals [var].type); - if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX && var <= G_MAXUINT16 && new_var <= G_MAXUINT16) { - // We store these in the instruction data slots so we do this optimizations only if they fit - pair_sregs [num_pairs] = (guint16)var; - pair_dregs [num_pairs] = (guint16)new_var; - num_pairs++; - // The arg of the call is no longer global - *call_args = new_var; - } else { - int opcode = get_mov_for_type (mt, FALSE); - InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); - interp_ins_set_dreg (new_inst, new_var); - interp_ins_set_sreg (new_inst, var); - if (opcode == MINT_MOV_VT) - new_inst->data [0] = GINT_TO_UINT16 (td->locals [var].size); - // The arg of the call is no longer global - *call_args = new_var; - // Also update liveness for this instruction - foreach_local_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb); - ins_index++; - } - } else { - // Flag this var as it has special storage on the call args stack - td->locals [var].call = ins; - td->locals [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - } - call_args++; - var = *call_args; - } - if (num_pairs > 0) { - int i; - for (i = 0; i < num_pairs; i++) { - set_var_live_range (td, pair_sregs [i], ins_index); - set_var_live_range (td, pair_dregs [i], ins_index); - } - if (num_pairs == 1) { - int mt = mono_mint_type (td->locals [pair_sregs [0]].type); - int opcode = get_mov_for_type (mt, FALSE); - InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); - interp_ins_set_dreg (new_inst, pair_dregs [0]); - interp_ins_set_sreg (new_inst, pair_sregs [0]); - } else { - // Squash together multiple moves to the param area into a single opcode - int opcode = MINT_MOV_8_2 + num_pairs - 2; - InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); - int k = 0; - for (i = 0; i < num_pairs; i++) { - new_inst->data [k++] = pair_dregs [i]; - new_inst->data [k++] = pair_sregs [i]; - } - } - ins_index++; - } - } - } - // Set live_start and live_end for every referenced local that is not global - foreach_local_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb); - ins_index++; - } - gint32 current_offset = td->total_locals_size; - - ins_index = 0; - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - gboolean is_call = ins->flags & INTERP_INST_FLAG_CALL; - - if (opcode == MINT_NOP) - continue; - - if (td->verbose_level) { - g_print ("\tins_index %d\t", ins_index); - dump_interp_inst (ins, td->data_items); - } - - // Expire source vars. We first mark them as not alive and then compact the array - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { - int var = ins->sregs [i]; - if (var == MINT_CALL_ARGS_SREG) - continue; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) { - g_assert (!(td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); - end_active_var (td, &av, var); - } - } - if (opcode >= MINT_MOV_8_2 && opcode <= MINT_MOV_8_4) { - // These opcodes have multiple dvars, which overcomplicate things, so they are - // marked as having no svars/dvars, for now. Special case it. - int num_pairs = 2 + opcode - MINT_MOV_8_2; - for (int i = 0; i < num_pairs; i++) { - int var = ins->data [2 * i + 1]; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) - end_active_var (td, &av, var); - } - } - - if (is_call) - end_active_call (td, &ac, ins); - - compact_active_vars (td, &av, ¤t_offset); - - // Alloc dreg local starting at the stack_offset - if (mono_interp_op_dregs [opcode]) { - int var = ins->dreg; - - if (td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - add_active_call (td, &ac, td->locals [var].call); - } else if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].offset == -1) { - alloc_var_offset (td, var, ¤t_offset); - if (current_offset > final_total_locals_size) - final_total_locals_size = current_offset; - - if (td->verbose_level) - g_print ("alloc var %d to offset %d\n", var, td->locals [var].offset); - - if (td->locals [var].live_end > ins_index) { - // if dreg is still used in the basic block, add it to the active list - add_active_var (td, &av, var); - } else { - current_offset = td->locals [var].offset; - } - } - } - if (td->verbose_level) - dump_active_vars (td, &av); - ins_index++; - } - } - final_total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); - - // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) - // then also update td->total_locals_size to account for this space. - td->param_area_offset = final_total_locals_size; - for (unsigned int i = 0; i < td->locals_size; i++) { - // These are allocated separately at the end of the stack - if (td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - td->locals [i].offset += td->param_area_offset; - final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size); - } - } - td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); -} - /* * Very few methods have localloc. Handle it separately to not impact performance * of other methods. We replace the normal return opcodes with opcodes that also @@ -11288,7 +8707,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG g_print ("Runtime method: %s %p\n", mono_method_full_name (method, TRUE), rtm); g_print ("Locals size %d\n", td->total_locals_size); g_print ("Calculated stack height: %d, stated height: %d\n", td->max_stack_height, header->max_stack); - dump_interp_code (td->new_code, td->new_code_end, td->data_items); + interp_dump_code (td->new_code, td->new_code_end, td->data_items); } /* Check if we use excessive stack space */ @@ -11371,7 +8790,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG g_free (td->clause_indexes); g_free (td->data_items); g_free (td->stack); - g_free (td->locals); + g_free (td->vars); g_free (td->local_ref_count); g_hash_table_destroy (td->data_hash); #ifdef ENABLE_EXPERIMENT_TIERED diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 2bfbe42390510e..4b2c5bb5992ade 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -15,17 +15,6 @@ // This instruction is protected by a clause #define INTERP_INST_FLAG_PROTECTED_NEWOBJ 128 -#define INTERP_LOCAL_FLAG_DEAD 1 -#define INTERP_LOCAL_FLAG_EXECUTION_STACK 2 -#define INTERP_LOCAL_FLAG_CALL_ARGS 4 -#define INTERP_LOCAL_FLAG_GLOBAL 8 -#define INTERP_LOCAL_FLAG_NO_CALL_ARGS 16 - -#define INTERP_LOCAL_FLAG_UNKNOWN_USE 32 -#define INTERP_LOCAL_FLAG_LOCAL_ONLY 64 -// We use this flag to avoid addition of align field in InterpLocal, for now -#define INTERP_LOCAL_FLAG_SIMD 128 - typedef struct _InterpInst InterpInst; typedef struct _InterpBasicBlock InterpBasicBlock; typedef struct _InterpCallInfo InterpCallInfo; @@ -36,22 +25,22 @@ typedef struct unsigned char type; unsigned char flags; /* - * The local associated with the value of this stack entry. Every time we push on - * the stack a new local is created. + * The var associated with the value of this stack entry. Every time we push on + * the stack a new var is created. */ - int local; + int var; /* The offset from the execution stack start where this is stored. Used by the fast offset allocator */ int offset; /* Saves how much stack this is using. It is a multiple of MINT_STACK_SLOT_SIZE*/ int size; } StackInfo; -#define LOCAL_VALUE_NONE 0 -#define LOCAL_VALUE_LOCAL 1 -#define LOCAL_VALUE_I4 2 -#define LOCAL_VALUE_I8 3 -#define LOCAL_VALUE_R4 4 -#define LOCAL_VALUE_NON_NULL 5 +#define VAR_VALUE_NONE 0 +#define VAR_VALUE_OTHER_VAR 1 +#define VAR_VALUE_I4 2 +#define VAR_VALUE_I8 3 +#define VAR_VALUE_R4 4 +#define VAR_VALUE_NON_NULL 5 // LocalValue contains data to construct an InterpInst that is equivalent with the contents // of the stack slot / local / argument. @@ -60,7 +49,7 @@ typedef struct { int type; // Holds the local index or the actual constant value union { - int local; + int var; gint32 i; gint64 l; float f; @@ -70,7 +59,7 @@ typedef struct { int def_index; // ref count for ins->dreg int ref_count; -} LocalValue; +} InterpVarValue; struct _InterpInst { guint16 opcode; @@ -179,7 +168,6 @@ typedef struct { typedef struct { MonoType *type; int mt; - int flags; int indirects; int offset; int size; @@ -200,7 +188,16 @@ typedef struct { // Only used during super instruction pass. InterpInst *def; }; -} InterpLocal; + + guint dead : 1; + guint execution_stack : 1; + guint call_args : 1; + guint global : 1; + guint no_call_args : 1; + guint unknown_use : 1; + guint local_only : 1; + guint simd : 1; // We use this flag to avoid addition of align field in InterpVar, for now +} InterpVar; typedef struct { @@ -225,13 +222,16 @@ typedef struct gint32 param_area_offset; gint32 total_locals_size; gint32 max_stack_size; - InterpLocal *locals; int dummy_var; int *local_ref_count; unsigned int il_locals_offset; unsigned int il_locals_size; - unsigned int locals_size; - unsigned int locals_capacity; + + // All vars, used in instructions + InterpVar *vars; + unsigned int vars_size; + unsigned int vars_capacity; + int n_data_items; int max_data_items; void **data_items; @@ -289,7 +289,7 @@ typedef struct #define interp_ins_set_dummy_dreg(ins,td) do { \ if (td->dummy_var < 0) \ - create_interp_dummy_var (td); \ + interp_create_dummy_var (td); \ ins->dreg = td->dummy_var; \ } while (0) @@ -381,6 +381,78 @@ mono_jiterp_insert_ins (TransformData *td, InterpInst *prev_ins, int opcode); void mono_interp_print_td_code (TransformData *td); +/* Compilation internal methods */ + +InterpInst* +interp_new_ins (TransformData *td, int opcode, int len); + +InterpInst* +interp_insert_ins_bb (TransformData *td, InterpBasicBlock *bb, InterpInst *prev_ins, int opcode); + +InterpInst* +interp_insert_ins (TransformData *td, InterpInst *prev_ins, int opcode); + +InterpInst* +interp_first_ins (InterpBasicBlock *bb); + +InterpInst* +interp_next_ins (InterpInst *ins); + +InterpInst* +interp_prev_ins (InterpInst *ins); + +void +interp_clear_ins (InterpInst *ins); + +gboolean +interp_ins_is_nop (InterpInst *ins); + +int +interp_get_ins_length (InterpInst *ins); + +void +interp_dump_ins (InterpInst *ins, gpointer *data_items); + +InterpInst* +interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int dreg); + +gint32 +interp_get_const_from_ldc_i4 (InterpInst *ins); + +int +interp_get_mov_for_type (int mt, gboolean needs_sext); + +gboolean +interp_is_short_offset (int src_offset, int dest_offset); + +InterpBasicBlock* +interp_alloc_bb (TransformData *td); + +void +interp_link_bblocks (TransformData *td, InterpBasicBlock *from, InterpBasicBlock *to); + +int +interp_compute_native_offset_estimates (TransformData *td); + +void +interp_optimize_code (TransformData *td); + +void +interp_alloc_offsets (TransformData *td); + +int +interp_alloc_global_var_offset (TransformData *td, int var); + +int +interp_create_var (TransformData *td, MonoType *type); + +void +interp_foreach_ins_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)); + +void +interp_foreach_ins_svar (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)); + + /* Forward definitions for simd methods */ static gboolean interp_emit_simd_intrinsics (TransformData *td, MonoMethod *cmethod, MonoMethodSignature *csignature, gboolean newobj); From 23eaf2568fd7d3d712654f1f7224eaef21819d43 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sat, 20 Jan 2024 11:33:53 -0500 Subject: [PATCH 143/189] Reduce defensive copy making in PublicKey (#97108) --- .../X509Certificates/PublicKey.cs | 22 +++++++++++++++---- .../X509Certificates/X509Certificate2.cs | 3 ++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs index e53f36d403e4b4..f8772e1ce9e87a 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs @@ -15,10 +15,24 @@ public sealed class PublicKey private AsymmetricAlgorithm? _key; public PublicKey(Oid oid, AsnEncodedData parameters, AsnEncodedData keyValue) + : this(oid, parameters, keyValue, skipCopy: false) + { + } + + internal PublicKey(Oid oid, AsnEncodedData parameters, AsnEncodedData keyValue, bool skipCopy) { _oid = oid; - EncodedParameters = new AsnEncodedData(parameters); - EncodedKeyValue = new AsnEncodedData(keyValue); + + if (skipCopy) + { + EncodedParameters = parameters; + EncodedKeyValue = keyValue; + } + else + { + EncodedParameters = new AsnEncodedData(parameters); + EncodedKeyValue = new AsnEncodedData(keyValue); + } } /// @@ -136,7 +150,7 @@ public static PublicKey CreateFromSubjectPublicKeyInfo(ReadOnlySpan source out AsnEncodedData localKeyValue); bytesRead = read; - return new PublicKey(localOid, localParameters, localKeyValue); + return new PublicKey(localOid, localParameters, localKeyValue, skipCopy: true); } /// @@ -312,7 +326,7 @@ internal static PublicKey DecodeSubjectPublicKeyInfo(ref SubjectPublicKeyInfoAsn out AsnEncodedData parameters, out AsnEncodedData keyValue); - return new PublicKey(oid, parameters, keyValue); + return new PublicKey(oid, parameters, keyValue, skipCopy: true); } private static void DecodeSubjectPublicKeyInfo( 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 650bea6130be9d..eb1a21fb98f1a0 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 @@ -310,7 +310,8 @@ public PublicKey PublicKey byte[] parameters = Pal.KeyAlgorithmParameters; byte[] keyValue = Pal.PublicKeyValue; Oid oid = new Oid(keyAlgorithmOid); - publicKey = _lazyPublicKey = new PublicKey(oid, new AsnEncodedData(oid, parameters), new AsnEncodedData(oid, keyValue)); + // PublicKey can use skipCopy because AsnEncodedData creates a defensive copy of the values. + publicKey = _lazyPublicKey = new PublicKey(oid, new AsnEncodedData(oid, parameters), new AsnEncodedData(oid, keyValue), skipCopy: true); } return publicKey; From 22149faa1e57efeb983a4284ce72535898a7ced6 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Sat, 20 Jan 2024 18:41:00 +0100 Subject: [PATCH 144/189] [coreclr] Block test that should not be run (#97248) --- .../CompareInfo/CompareInfoTests.HashCode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs index a2247b39f636ec..6fe89b75e6199a 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.HashCode.cs @@ -121,7 +121,7 @@ public static IEnumerable CheckHashingOfSkippedChars_TestData() yield return new object[] { '\u2029', thaiCmpInfo }; // ParagraphSeparator: PARAGRAPH SEPARATOR } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] [MemberData(nameof(CheckHashingOfSkippedChars_TestData))] public void CheckHashingOfSkippedChars(char character, CompareInfo cmpInfo) { From 27286aea39fd1cb662b8d80d87e6b3e6aa85a06c Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Sat, 20 Jan 2024 10:06:17 -0800 Subject: [PATCH 145/189] Stop tracking `genReturnBB` after global morph (#97130) * Stop tracking `genReturnBB` after global morph The `genReturnBB` pointer is set if a merged return block is created during `fgAddInternal` (so, quite early during compilation). It needs to be maintained (and the unreferenced block it points to needs to be kept around) until global morph, which hooks up flow to this block. After global morph, clear the pointer and don't maintain it; it is no longer needed. Later code that was checking for it can instead check for `BBJ_RETURN` blocks or `GT_RETURN` nodes. Also, remove the marking of the `genReturnBB` block as `BBF_DONT_REMOVE`. This leads to diffs, as unreachable return blocks get deleted. This can happen, say, if all other exits are tail calls. If that happens and we're in a case of needing a profiler hook, then the profile exit hook is also not needed (it would be unreachable). * Allow x86 synchronized methods to have unreachable return blocks The JIT still needs to report an end-of-synchronized-range offset in the GC info for x86 synchronized methods, so just create a new label at the end of all blocks. The VM will automatically exit the synchronization on an exceptional method exit. * Create final IG before clearing GC info --- src/coreclr/gcdump/i386/gcdumpx86.cpp | 3 ++- src/coreclr/jit/codegencommon.cpp | 10 ++++++---- src/coreclr/jit/codegenlinear.cpp | 19 +++++++++++++++++++ src/coreclr/jit/codegenxarch.cpp | 8 ++++---- src/coreclr/jit/compiler.cpp | 6 ++++++ src/coreclr/jit/compiler.h | 6 ++---- src/coreclr/jit/compiler.hpp | 7 ++++--- src/coreclr/jit/emit.cpp | 9 ++------- src/coreclr/jit/fgbasic.cpp | 20 -------------------- src/coreclr/jit/fgdiagnostic.cpp | 4 ++-- src/coreclr/jit/flowgraph.cpp | 19 +++++++------------ src/coreclr/jit/gcencode.cpp | 5 +++-- src/coreclr/jit/lower.cpp | 4 ++-- src/coreclr/jit/morph.cpp | 8 ++++++++ 14 files changed, 67 insertions(+), 61 deletions(-) diff --git a/src/coreclr/gcdump/i386/gcdumpx86.cpp b/src/coreclr/gcdump/i386/gcdumpx86.cpp index 1bf3e61011bd39..c5d359aac517e4 100644 --- a/src/coreclr/gcdump/i386/gcdumpx86.cpp +++ b/src/coreclr/gcdump/i386/gcdumpx86.cpp @@ -154,7 +154,8 @@ size_t GCDump::DumpInfoHdr (PTR_CBYTE gcInfoBlock, gcPrintf(" GuardStack cookie = [%s%u]\n", header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset); if (header->syncStartOffset != INVALID_SYNC_OFFSET) - gcPrintf(" Sync region = [%u,%u]\n", + gcPrintf(" Sync region = [%u,%u] ([0x%x,0x%x])\n", + header->syncStartOffset, header->syncEndOffset, header->syncStartOffset, header->syncEndOffset); if (header->epilogCount > 1 || (header->epilogCount != 0 && diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index db035461a31369..b12b584038c530 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -7499,6 +7499,7 @@ void CodeGen::genLongReturn(GenTree* treeNode) //------------------------------------------------------------------------ // genReturn: Generates code for return statement. // In case of struct return, delegates to the genStructReturn method. +// In case of LONG return on 32-bit, delegates to the genLongReturn method. // // Arguments: // treeNode - The GT_RETURN or GT_RETFILT tree node. @@ -7508,7 +7509,8 @@ void CodeGen::genLongReturn(GenTree* treeNode) // void CodeGen::genReturn(GenTree* treeNode) { - assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT); + assert(treeNode->OperIs(GT_RETURN, GT_RETFILT)); + GenTree* op1 = treeNode->gtGetOp1(); var_types targetType = treeNode->TypeGet(); @@ -7609,9 +7611,9 @@ void CodeGen::genReturn(GenTree* treeNode) // maintain such an invariant irrespective of whether profiler hook needed or not. // Also, there is not much to be gained by materializing it as an explicit node. // - // There should be a single return block while generating profiler ELT callbacks, - // so we just look for that block to trigger insertion of the profile hook. - if ((compiler->compCurBB == compiler->genReturnBB) && compiler->compIsProfilerHookNeeded()) + // There should be a single GT_RETURN while generating profiler ELT callbacks. + // + if (treeNode->OperIs(GT_RETURN) && compiler->compIsProfilerHookNeeded()) { // !! NOTE !! // Since we are invalidating the assumption that we would slip into the epilog diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 81c84a10f45a20..67f4bb73c0eb18 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -835,6 +835,25 @@ void CodeGen::genCodeForBBlist() #endif // DEBUG } //------------------ END-FOR each block of the method ------------------- +#if !defined(FEATURE_EH_FUNCLETS) + // If this is a synchronized method on x86, and we generated all the code without + // generating the "exit monitor" call, then we must have deleted the single return block + // with that call because it was dead code. We still need to report the monitor range + // to the VM in the GC info, so create a label at the very end so we have a marker for + // the monitor end range. + // + // Do this before cleaning the GC refs below; we don't want to create an IG that clears + // the `this` pointer for lvaKeepAliveAndReportThis. + + if ((compiler->info.compFlags & CORINFO_FLG_SYNCH) && (compiler->syncEndEmitCookie == nullptr)) + { + JITDUMP("Synchronized method with missing exit monitor call; adding final label\n"); + compiler->syncEndEmitCookie = + GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur); + noway_assert(compiler->syncEndEmitCookie != nullptr); + } +#endif // !FEATURE_EH_FUNCLETS + // There could be variables alive at this point. For example see lvaKeepAliveAndReportThis. // This call is for cleaning the GC refs genUpdateLife(VarSetOps::MakeEmpty(compiler)); diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index e44c4247fe5521..43c006291dd3e5 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -6176,17 +6176,17 @@ void CodeGen::genCall(GenTreeCall* call) { case CORINFO_HELP_MON_ENTER: case CORINFO_HELP_MON_ENTER_STATIC: - noway_assert(compiler->syncStartEmitCookie == NULL); + noway_assert(compiler->syncStartEmitCookie == nullptr); compiler->syncStartEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur); - noway_assert(compiler->syncStartEmitCookie != NULL); + noway_assert(compiler->syncStartEmitCookie != nullptr); break; case CORINFO_HELP_MON_EXIT: case CORINFO_HELP_MON_EXIT_STATIC: - noway_assert(compiler->syncEndEmitCookie == NULL); + noway_assert(compiler->syncEndEmitCookie == nullptr); compiler->syncEndEmitCookie = GetEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur); - noway_assert(compiler->syncEndEmitCookie != NULL); + noway_assert(compiler->syncEndEmitCookie != nullptr); break; default: break; diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 3efa0e844b0141..9e88c6ab091305 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -3332,6 +3332,12 @@ void Compiler::compInitOptions(JitFlags* jitFlags) printf("OPTIONS: compProcedureSplitting = %s\n", dspBool(opts.compProcedureSplitting)); printf("OPTIONS: compProcedureSplittingEH = %s\n", dspBool(opts.compProcedureSplittingEH)); + // This is rare; don't clutter up the dump with it normally. + if (compProfilerHookNeeded) + { + printf("OPTIONS: compProfilerHookNeeded = %s\n", dspBool(compProfilerHookNeeded)); + } + if (jitFlags->IsSet(JitFlags::JIT_FLAG_BBOPT)) { printf("OPTIONS: optimizer should use profile data\n"); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 36c8725b3d143f..80972c4c36ca6a 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5358,8 +5358,6 @@ class Compiler IL_OFFSET fgFindBlockILOffset(BasicBlock* block); void fgFixEntryFlowForOSR(); - void fgUpdateSingleReturnBlock(BasicBlock* block); - BasicBlock* fgSplitBlockAtBeginning(BasicBlock* curr); BasicBlock* fgSplitBlockAtEnd(BasicBlock* curr); BasicBlock* fgSplitBlockAfterStatement(BasicBlock* curr, Statement* stmt); @@ -10745,14 +10743,14 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX unsigned compHndBBtabCount; // element count of used elements in EH data array unsigned compHndBBtabAllocCount; // element count of allocated elements in EH data array -#if defined(TARGET_X86) +#if !defined(FEATURE_EH_FUNCLETS) //------------------------------------------------------------------------- // Tracking of region covered by the monitor in synchronized methods void* syncStartEmitCookie; // the emitter cookie for first instruction after the call to MON_ENTER void* syncEndEmitCookie; // the emitter cookie for first instruction after the call to MON_EXIT -#endif // !TARGET_X86 +#endif // !FEATURE_EH_FUNCLETS Phases mostRecentlyActivePhase; // the most recently active phase PhaseChecks activePhaseChecks; // the currently active phase checks diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index a686de150d5fe0..b00f3e66139216 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -2320,7 +2320,7 @@ inline void LclVarDsc::incRefCnts(weight_t weight, Compiler* comp, RefCountState inline bool Compiler::lvaKeepAliveAndReportThis() { - if (info.compIsStatic || lvaTable[0].TypeGet() != TYP_REF) + if (info.compIsStatic || (lvaTable[0].TypeGet() != TYP_REF)) { return false; } @@ -2354,7 +2354,7 @@ inline bool Compiler::lvaKeepAliveAndReportThis() // We keep it alive in the lookup scenario, even when the VM didn't ask us to, // because collectible types need the generics context when gc-ing. // - // Methoods that can inspire OSR methods must always report context as live + // Methods that can inspire OSR methods must always report context as live // if (genericsContextIsThis) { @@ -4977,8 +4977,9 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos dfsFrom(fgEntryBB); } - if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum) && !fgGlobalMorphDone) + if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum)) { + assert(!fgGlobalMorphDone); // We introduce the merged return BB before morph and will redirect // other returns to it as part of morph; keep it reachable. dfsFrom(genReturnBB); diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index b993c29e1dda31..b6adc24d275206 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -2333,16 +2333,11 @@ void emitter::emitGeneratePrologEpilog() void emitter::emitStartPrologEpilogGeneration() { - /* Save the current IG if it's non-empty */ - - if (emitCurIGnonEmpty()) + // Save the current IG if we have one. It might be empty if we added an end-of-compilation label. + if (emitCurIG != nullptr) { emitSavIG(); } - else - { - assert(emitCurIG == nullptr); - } } /***************************************************************************** diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index a7a7cc0b5d980d..d88ed59a8f1963 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -4755,24 +4755,6 @@ IL_OFFSET Compiler::fgFindBlockILOffset(BasicBlock* block) return BAD_IL_OFFSET; } -//------------------------------------------------------------------------------ -// fgUpdateSingleReturnBlock : A block has been split. If it was the single return -// block, then update the single return block pointer. -// -// Arguments: -// block - The block that was split -// -void Compiler::fgUpdateSingleReturnBlock(BasicBlock* block) -{ - assert(block->KindIs(BBJ_ALWAYS)); - if (genReturnBB == block) - { - assert(block->GetTarget()->KindIs(BBJ_RETURN)); - JITDUMP("Updating genReturnBB from " FMT_BB " to " FMT_BB "\n", block->bbNum, block->GetTarget()->bbNum); - genReturnBB = block->GetTarget(); - } -} - //------------------------------------------------------------------------------ // fgSplitBlockAtEnd - split the given block into two blocks. // All code in the block stays in the original block. @@ -4849,8 +4831,6 @@ BasicBlock* Compiler::fgSplitBlockAtEnd(BasicBlock* curr) fgAddRefPred(newBlock, curr); - fgUpdateSingleReturnBlock(curr); - return newBlock; } diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 1b8ed455ae5a67..c0f1ade1d077e4 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -2255,10 +2255,10 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 * } } - // Indicate if it's the single return block + // Indicate if it's the merged return block. if (block == genReturnBB) { - printf(" one-return"); + printf(" merged-return"); } printf("\n"); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 1b33f593581335..e65d161b8813a1 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1868,7 +1868,7 @@ class MergedReturns return; } - // We'e reached our threshold + // We've reached our threshold mergingReturns = true; // Merge any returns we've already identified @@ -2478,29 +2478,25 @@ PhaseStatus Compiler::fgAddInternal() if (info.compFlags & CORINFO_FLG_SYNCH) { - GenTree* tree = NULL; + GenTree* tree = nullptr; /* Insert the expression "enterCrit(this)" or "enterCrit(handle)" */ if (info.compIsStatic) { tree = fgGetCritSectOfStaticMethod(); - tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER_STATIC, TYP_VOID, tree); } else { noway_assert(lvaTable[info.compThisArg].lvType == TYP_REF); - tree = gtNewLclvNode(info.compThisArg, TYP_REF); - tree = gtNewHelperCallNode(CORINFO_HELP_MON_ENTER, TYP_VOID, tree); } /* Create a new basic block and stick the call in it */ fgEnsureFirstBBisScratch(); - fgNewStmtAtEnd(fgFirstBB, tree); #ifdef DEBUG @@ -2522,13 +2518,11 @@ PhaseStatus Compiler::fgAddInternal() if (info.compIsStatic) { tree = fgGetCritSectOfStaticMethod(); - tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT_STATIC, TYP_VOID, tree); } else { tree = gtNewLclvNode(info.compThisArg, TYP_REF); - tree = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, tree); } @@ -2537,15 +2531,16 @@ PhaseStatus Compiler::fgAddInternal() #ifdef DEBUG if (verbose) { - printf("\nSynchronized method - Add exit expression "); - printTreeID(tree); + printf("\nSynchronized method - Add exitCrit statement in single return block %s\n", + genReturnBB->dspToString()); + gtDispTree(tree); printf("\n"); } #endif // Reset cookies used to track start and end of the protected region in synchronized methods - syncStartEmitCookie = NULL; - syncEndEmitCookie = NULL; + syncStartEmitCookie = nullptr; + syncEndEmitCookie = nullptr; madeChanges = true; } diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index ec92b88605f15a..ad75dbf6270993 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -1563,15 +1563,16 @@ size_t GCInfo::gcInfoBlockHdrSave( header->syncStartOffset = INVALID_SYNC_OFFSET; header->syncEndOffset = INVALID_SYNC_OFFSET; + #ifndef UNIX_X86_ABI // JIT is responsible for synchronization on funclet-based EH model that x86/Linux uses. if (compiler->info.compFlags & CORINFO_FLG_SYNCH) { - assert(compiler->syncStartEmitCookie != NULL); + assert(compiler->syncStartEmitCookie != nullptr); header->syncStartOffset = compiler->GetEmitter()->emitCodeOffset(compiler->syncStartEmitCookie, 0); assert(header->syncStartOffset != INVALID_SYNC_OFFSET); - assert(compiler->syncEndEmitCookie != NULL); + assert(compiler->syncEndEmitCookie != nullptr); header->syncEndOffset = compiler->GetEmitter()->emitCodeOffset(compiler->syncEndEmitCookie, 0); assert(header->syncEndOffset != INVALID_SYNC_OFFSET); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index dd5fc6b3673bac..27079d52e3bfb9 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4271,7 +4271,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret) } // Method doing PInvokes has exactly one return block unless it has tail calls. - if (comp->compMethodRequiresPInvokeFrame() && (comp->compCurBB == comp->genReturnBB)) + if (comp->compMethodRequiresPInvokeFrame()) { InsertPInvokeMethodEpilog(comp->compCurBB DEBUGARG(ret)); } @@ -5369,7 +5369,7 @@ void Lowering::InsertPInvokeMethodEpilog(BasicBlock* returnBB DEBUGARG(GenTree* JITDUMP("======= Inserting PInvoke method epilog\n"); // Method doing PInvoke calls has exactly one return block unless it has "jmp" or tail calls. - assert(((returnBB == comp->genReturnBB) && returnBB->KindIs(BBJ_RETURN)) || returnBB->endsWithTailCallOrJmp(comp)); + assert(returnBB->KindIs(BBJ_RETURN) || returnBB->endsWithTailCallOrJmp(comp)); LIR::Range& returnBlockRange = LIR::AsRange(returnBB); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index ca7c46ef6654dd..1bf3288fa7c3c2 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -14140,6 +14140,14 @@ PhaseStatus Compiler::fgMorphBlocks() fgEntryBB = nullptr; } + // We don't maintain `genReturnBB` after this point. + if (genReturnBB != nullptr) + { + // It no longer needs special "keep" treatment. + genReturnBB->RemoveFlags(BBF_DONT_REMOVE); + genReturnBB = nullptr; + } + // We are done with the global morphing phase // fgInvalidateDfsTree(); From 7ac5caf91252c6d6689351d436aa6c118339e95b Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Sat, 20 Jan 2024 13:28:56 -0500 Subject: [PATCH 146/189] JIT: Remove Compiler::fgIsBetterFallThrough (#97222) --- src/coreclr/jit/compiler.h | 2 - src/coreclr/jit/fgbasic.cpp | 100 ++---------------------------------- 2 files changed, 3 insertions(+), 99 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 80972c4c36ca6a..5bf68dfff8bbdb 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5927,8 +5927,6 @@ class Compiler void fgFindBasicBlocks(); - bool fgIsBetterFallThrough(BasicBlock* bCur, BasicBlock* bAlt); - bool fgCheckEHCanInsertAfterBlock(BasicBlock* blk, unsigned regionIndex, bool putInTryRegion); BasicBlock* fgFindInsertPoint(unsigned regionIndex, diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index d88ed59a8f1963..6acdd220f20b0c 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -6378,83 +6378,6 @@ void Compiler::fgInsertBBafter(BasicBlock* insertAfterBlk, BasicBlock* newBlk) insertAfterBlk->SetNext(newBlk); } -// We have two edges (bAlt => bCur) and (bCur => bNext). -// -// Returns true if the weight of (bAlt => bCur) -// is greater than the weight of (bCur => bNext). -// We compare the edge weights if we have valid edge weights -// otherwise we compare blocks weights. -// -bool Compiler::fgIsBetterFallThrough(BasicBlock* bCur, BasicBlock* bAlt) -{ - // bCur can't be NULL and must be a fall through bbKind - noway_assert(bCur != nullptr); - noway_assert(bCur->bbFallsThrough() || (bCur->KindIs(BBJ_ALWAYS) && bCur->JumpsToNext())); - noway_assert(bAlt != nullptr); - - if (bAlt->KindIs(BBJ_ALWAYS)) - { - // If bAlt doesn't jump to bCur, it can't be a better fall through than bCur - if (!bAlt->TargetIs(bCur)) - { - return false; - } - } - else if (bAlt->KindIs(BBJ_COND)) - { - // If bAlt doesn't potentially jump to bCur, it can't be a better fall through than bCur - if (!bAlt->TrueTargetIs(bCur)) - { - return false; - } - } - else - { - // We only handle the cases when bAlt is a BBJ_ALWAYS or a BBJ_COND - return false; - } - - // BBJ_CALLFINALLY shouldn't have a flow edge into the next (BBJ_CALLFINALLYRET) block - if (bCur->isBBCallFinallyPair()) - { - return false; - } - - // Currently bNext is the fall through for bCur - // TODO-NoFallThrough: Allow bbFalseTarget to diverge from bbNext for BBJ_COND - assert(!bCur->KindIs(BBJ_COND) || bCur->NextIs(bCur->GetFalseTarget())); - BasicBlock* bNext = bCur->Next(); - noway_assert(bNext != nullptr); - - // We will set result to true if bAlt is a better fall through than bCur - bool result; - if (fgHaveValidEdgeWeights) - { - // We will compare the edge weight for our two choices - FlowEdge* edgeFromAlt = fgGetPredForBlock(bCur, bAlt); - FlowEdge* edgeFromCur = fgGetPredForBlock(bNext, bCur); - noway_assert(edgeFromCur != nullptr); - noway_assert(edgeFromAlt != nullptr); - - result = (edgeFromAlt->edgeWeightMin() > edgeFromCur->edgeWeightMax()); - } - else - { - if (bAlt->KindIs(BBJ_ALWAYS)) - { - // Our result is true if bAlt's weight is more than bCur's weight - result = (bAlt->bbWeight > bCur->bbWeight); - } - else - { - noway_assert(bAlt->KindIs(BBJ_COND)); - // Our result is true if bAlt's weight is more than twice bCur's weight - result = (bAlt->bbWeight > (2 * bCur->bbWeight)); - } - } - return result; -} - //------------------------------------------------------------------------ // Finds the block closest to endBlk in the range [startBlk..endBlk) after which a block can be // inserted easily. Note that endBlk cannot be returned; its predecessor is the last block that can @@ -6663,32 +6586,15 @@ BasicBlock* Compiler::fgFindInsertPoint(unsigned regionIndex, } } - // Look for an insert location: - // 1. We want blocks that don't end with a fall through, - // 2. Also, when blk equals nearBlk we may want to insert here. + // Look for an insert location. We want blocks that don't end with a fall through. // Quirk: Manually check for BBJ_COND fallthrough behavior const bool blkFallsThrough = blk->bbFallsThrough() && (!blk->KindIs(BBJ_COND) || blk->NextIs(blk->GetFalseTarget())); const bool blkJumpsToNext = blk->KindIs(BBJ_ALWAYS) && blk->HasFlag(BBF_NONE_QUIRK) && blk->JumpsToNext(); - if ((!blkFallsThrough && !blkJumpsToNext) || (blk == nearBlk)) + if (!blkFallsThrough && !blkJumpsToNext) { bool updateBestBlk = true; // We will probably update the bestBlk - // If blk falls through then we must decide whether to use the nearBlk - // hint - if (blkFallsThrough || blkJumpsToNext) - { - noway_assert(blk == nearBlk); - if (jumpBlk != nullptr) - { - updateBestBlk = fgIsBetterFallThrough(blk, jumpBlk); - } - else - { - updateBestBlk = false; - } - } - // If we already have a best block, see if the 'runRarely' flags influences // our choice. If we want a runRarely insertion point, and the existing best // block is run rarely but the current block isn't run rarely, then don't @@ -6697,7 +6603,7 @@ BasicBlock* Compiler::fgFindInsertPoint(unsigned regionIndex, // want a non-rarely-run block), but bestBlock->isRunRarely() is true. In that // case, we should update the block, also. Probably what we want is: // (bestBlk->isRunRarely() != runRarely) && (blk->isRunRarely() == runRarely) - if (updateBestBlk && (bestBlk != nullptr) && runRarely && bestBlk->isRunRarely() && !blk->isRunRarely()) + if ((bestBlk != nullptr) && runRarely && bestBlk->isRunRarely() && !blk->isRunRarely()) { updateBestBlk = false; } From db3b80ff0a5f7f0165241c00a998d81c22200cf6 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Sat, 20 Jan 2024 15:25:14 -0800 Subject: [PATCH 147/189] Frozen objects should be identified as Gen2 in the handle table (#97109) --- src/coreclr/gc/env/common.h | 1 + src/coreclr/gc/handletable.cpp | 10 +++++++++- src/coreclr/gc/handletablepriv.h | 12 ++++++++++++ src/coreclr/gc/handletablescan.cpp | 6 +++--- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/coreclr/gc/env/common.h b/src/coreclr/gc/env/common.h index 02e142a23a602d..78562ef0438b40 100644 --- a/src/coreclr/gc/env/common.h +++ b/src/coreclr/gc/env/common.h @@ -21,6 +21,7 @@ #include #include #include +#include #include diff --git a/src/coreclr/gc/handletable.cpp b/src/coreclr/gc/handletable.cpp index ca8b1dbaa61cb4..356b0e0bdc314b 100644 --- a/src/coreclr/gc/handletable.cpp +++ b/src/coreclr/gc/handletable.cpp @@ -24,6 +24,14 @@ DWORD g_dwHandles = 0; #endif // ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE +#ifndef DACCESS_COMPILE +int GetConvertedGeneration(_UNCHECKED_OBJECTREF obj) +{ + int generation = g_theGCHeap->WhichGeneration(obj); + return generation == INT_MAX ? max_generation : generation; +} +#endif //DACCESS_COMPILE + /**************************************************************************** * * FORWARD DECLARATIONS @@ -577,7 +585,7 @@ void HndWriteBarrierWorker(OBJECTHANDLE handle, _UNCHECKED_OBJECTREF value) if (*pClumpAge != 0) // Perf optimization: if clumpAge is 0, nothing more to do { // find out generation - int generation = g_theGCHeap->WhichGeneration(value); + int generation = GetConvertedGeneration(value); uint32_t uType = HandleFetchType(handle); #ifdef FEATURE_ASYNC_PINNED_HANDLES diff --git a/src/coreclr/gc/handletablepriv.h b/src/coreclr/gc/handletablepriv.h index 086ef2e018b6fa..e09e89cecaaae5 100644 --- a/src/coreclr/gc/handletablepriv.h +++ b/src/coreclr/gc/handletablepriv.h @@ -942,3 +942,15 @@ void CALLBACK BlockVerifyAgeMapForBlocks(PTR_TableSegment pSegment, uint32_t uBl PTR_TableSegment CALLBACK xxxAsyncSegmentIterator(PTR_HandleTable pTable, TableSegment *pPrevSegment, CrstHolderWithState *pCrstHolder); /*--------------------------------------------------------------------------*/ + +#ifndef DACCESS_COMPILE + +/* + * GetConvertedGeneration + * + * Get the generation of an object, where a frozen object is regarded as max_generation + * + */ +int GetConvertedGeneration(_UNCHECKED_OBJECTREF obj); + +#endif //DACCESS_COMPILE diff --git a/src/coreclr/gc/handletablescan.cpp b/src/coreclr/gc/handletablescan.cpp index 171647501b4cb9..7c0692ad5b8ff4 100644 --- a/src/coreclr/gc/handletablescan.cpp +++ b/src/coreclr/gc/handletablescan.cpp @@ -811,7 +811,7 @@ void BlockResetAgeMapForBlocksWorker(uint32_t *pdwGen, uint32_t dwClumpMask, Sca { if (!HndIsNullOrDestroyedHandle(*pValue)) { - int thisAge = g_theGCHeap->WhichGeneration(*pValue); + int thisAge = GetConvertedGeneration(*pValue); if (minAge > thisAge) minAge = thisAge; @@ -820,7 +820,7 @@ void BlockResetAgeMapForBlocksWorker(uint32_t *pdwGen, uint32_t dwClumpMask, Sca [](Object*, Object* to, void* ctx) { int* minAge = reinterpret_cast(ctx); - int generation = g_theGCHeap->WhichGeneration(to); + int generation = GetConvertedGeneration(to); if (*minAge > generation) { *minAge = generation; @@ -903,7 +903,7 @@ static void VerifyObjectAndAge(_UNCHECKED_OBJECTREF from, _UNCHECKED_OBJECTREF o { VerifyObject(from, obj); - int thisAge = g_theGCHeap->WhichGeneration(obj); + int thisAge = GetConvertedGeneration(obj); //debugging code //if (minAge > thisAge && thisAge < g_theGCHeap->GetMaxGeneration()) From b1e85b8fa386e4534eed0ce4dd394b57b146575f Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 20 Jan 2024 16:56:05 -0800 Subject: [PATCH 148/189] Rewrite offending test (#97118) * Remove active issue --- src/tests/Interop/CMakeLists.txt | 1 - src/tests/Interop/Interop.csproj | 1 - .../MarshalAPI/IUnknown/CMakeLists.txt | 12 -- .../MarshalAPI/IUnknown/IUnknownNative.cpp | 14 -- .../MarshalAPI/IUnknown/IUnknownTest.cs | 143 +++--------------- .../Miscellaneous/HandleRef/HandleRefTest.cs | 2 - 6 files changed, 23 insertions(+), 150 deletions(-) delete mode 100644 src/tests/Interop/MarshalAPI/IUnknown/CMakeLists.txt delete mode 100644 src/tests/Interop/MarshalAPI/IUnknown/IUnknownNative.cpp diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 21e83b1f9f370f..f77b8b469d7733 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -65,7 +65,6 @@ add_subdirectory(DisabledRuntimeMarshalling) add_subdirectory(MonoAPI/Native) if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(ExecInDefAppDom) - add_subdirectory(MarshalAPI/IUnknown) add_subdirectory(PInvoke/Attributes/LCID) add_subdirectory(PInvoke/BestFitMapping/Char) add_subdirectory(PInvoke/BestFitMapping/LPStr) diff --git a/src/tests/Interop/Interop.csproj b/src/tests/Interop/Interop.csproj index bbf2be1ba01734..6fc22f7bf4f273 100644 --- a/src/tests/Interop/Interop.csproj +++ b/src/tests/Interop/Interop.csproj @@ -48,7 +48,6 @@ - diff --git a/src/tests/Interop/MarshalAPI/IUnknown/CMakeLists.txt b/src/tests/Interop/MarshalAPI/IUnknown/CMakeLists.txt deleted file mode 100644 index 6c6400c297127b..00000000000000 --- a/src/tests/Interop/MarshalAPI/IUnknown/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -project (IUnknownNative) -include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") -set(SOURCES IUnknownNative.cpp) - -# add the executable -add_library (IUnknownNative SHARED ${SOURCES}) -target_link_libraries(IUnknownNative PRIVATE ${LINK_LIBRARIES_ADDITIONAL}) - -# add the install targets -install (TARGETS IUnknownNative DESTINATION bin) - - diff --git a/src/tests/Interop/MarshalAPI/IUnknown/IUnknownNative.cpp b/src/tests/Interop/MarshalAPI/IUnknown/IUnknownNative.cpp deleted file mode 100644 index 794dbf52a6dc0f..00000000000000 --- a/src/tests/Interop/MarshalAPI/IUnknown/IUnknownNative.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -#include -#include - -extern "C" DLL_EXPORT BOOL __cdecl Marshal_IUnknown(/*[in]*/IUnknown *o) -{ - //Call AddRef and Release on the passed IUnknown - //test if the ref counts get updated as expected - uint32_t refCount = o->AddRef(); - if((refCount-1) != o->Release()) - return FALSE; - return TRUE; -} diff --git a/src/tests/Interop/MarshalAPI/IUnknown/IUnknownTest.cs b/src/tests/Interop/MarshalAPI/IUnknown/IUnknownTest.cs index eda2520c25d478..7507c6945f9a1e 100644 --- a/src/tests/Interop/MarshalAPI/IUnknown/IUnknownTest.cs +++ b/src/tests/Interop/MarshalAPI/IUnknown/IUnknownTest.cs @@ -29,22 +29,12 @@ public override string ToString() public class IUnknownMarshalingTest { - [DllImport(@"IUnknownNative", CallingConvention = CallingConvention.Cdecl)] - private static extern bool Marshal_IUnknown([In]IntPtr ptr); - private object[] TestObjects; - public void GetIUnknownForObjectTest() + public unsafe void GetIUnknownForObjectTest() { - - try - { - //test null - IntPtr nullPtr = Marshal.GetIUnknownForObject(null); - } - catch (ArgumentNullException) { } - - + // Test null + Assert.Throws(() => Marshal.GetIUnknownForObject(null)); foreach (object obj in TestObjects) { IntPtr ptr = IntPtr.Zero; @@ -53,82 +43,36 @@ public void GetIUnknownForObjectTest() { ptr = Marshal.GetIUnknownForObject(obj); - if (!Marshal_IUnknown(ptr)) + // Validate IUnknown AddRef/Release usage + int plusOne = Marshal.AddRef(ptr); + int count = Marshal.Release(ptr); + if ((plusOne - 1) != count) { - throw new Exception("Failure on native side. Ref counts do not work as expected"); + throw new Exception("Ref counts do not work as expected"); } - } - finally - { - if (ptr != IntPtr.Zero) - Marshal.Release(ptr); - } - } - } - public void GetComInterfaceForObjectTest() - { - - //test null - IntPtr nullPtr = Marshal.GetComInterfaceForObject(null, typeof(object)); - if (nullPtr != IntPtr.Zero) - throw new Exception("A valid ptr was returned for null object."); - - foreach (object obj in TestObjects) - { - IntPtr ptr = IntPtr.Zero; - - try - { - ptr = Marshal.GetComInterfaceForObject(obj, typeof(object)); - - if (!Marshal_IUnknown(ptr)) + // Validate IUnknown QueryInterface usage + Guid noIID = new("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"); + int hr = Marshal.QueryInterface(ptr, noIID, out IntPtr _); + if (hr == 0) { - throw new Exception("Failure on native side. Ref counts do not work as expected"); + throw new Exception("QueryInterface() does not work as expected"); } } finally { if (ptr != IntPtr.Zero) - Marshal.Release(ptr); - } - } - } - - public void GetComInterfaceForObjectQueryInterfaceTest() - { - IntPtr nullPtr = Marshal.GetComInterfaceForObject(null, typeof(object), CustomQueryInterfaceMode.Allow); - if (nullPtr != IntPtr.Zero) - throw new Exception("A valid ptr was returned for null object."); - - foreach (object obj in TestObjects) - { - IntPtr ptr = IntPtr.Zero; - ptr = Marshal.GetComInterfaceForObject(obj, typeof(object), CustomQueryInterfaceMode.Allow); - try - { - if (!Marshal_IUnknown(ptr)) { - throw new Exception("Failure on native side. Ref counts do not work as expected"); - } - } - finally - { - if (ptr != IntPtr.Zero) Marshal.Release(ptr); + } } } } public void GetObjectForIUnknownTest() { - try - { - //test IntPtr.Zero - Object nullObj = Marshal.GetObjectForIUnknown(IntPtr.Zero); - - } - catch (ArgumentNullException) { } + // Test null + Assert.Throws(() => Marshal.GetObjectForIUnknown(IntPtr.Zero)); foreach (object obj in TestObjects) { @@ -138,69 +82,31 @@ public void GetObjectForIUnknownTest() { ptr = Marshal.GetIUnknownForObject(obj); - Object tmpObj = Marshal.GetObjectForIUnknown(ptr); + var tmpObj = Marshal.GetObjectForIUnknown(ptr); //compare the new object reference with the original object, they should point to the same underlying object if (!object.ReferenceEquals(obj, tmpObj)) + { throw new Exception("GetObjectForIUnknown returned a different object. Original: " + obj + ", New: " + tmpObj); + } } finally { if (ptr != IntPtr.Zero) + { Marshal.Release(ptr); + } } } } - public void GetUniqueObjectForIUnknownTest() - { - - //test IntPtr.Zero - Object nullObj = Marshal.GetUniqueObjectForIUnknown(IntPtr.Zero); - - if (nullObj != null) - throw new Exception("Object returned for IntPtr.Zero is not null."); - - - foreach (object obj in TestObjects) - { - IntPtr ptr = IntPtr.Zero; - object tmpObj = null; - - try - { - ptr = Marshal.GetIUnknownForObject(obj); - - tmpObj = Marshal.GetUniqueObjectForIUnknown(ptr); - - //compare the new object reference with the original object, they should point to differnet objects - if (object.ReferenceEquals(obj, tmpObj)) - throw new Exception("GetUniqueObjectForIUnknown returned the original object"); - - //The value should be the same - if (!obj.Equals(tmpObj)) - throw new Exception("GetUniqueObjectForIUnknown returned an object with different value. Original: " + obj + ", New: " + tmpObj); - - } - finally - { - if (tmpObj != null) - Marshal.ReleaseComObject(tmpObj); - if (ptr != IntPtr.Zero) - Marshal.Release(ptr); - } - } - } - - public bool RunTests() + public void RunTests() { - Initialize(); GetIUnknownForObjectTest(); GetObjectForIUnknownTest(); - return true; } - public bool Initialize() + public void Initialize() { TestObjects = new object[7]; TestObjects[0] = 1; //int @@ -210,12 +116,10 @@ public bool Initialize() TestObjects[4] = new TestClass(); //Object of type TestClass TestObjects[5] = new List(); //Projected Type TestObjects[6] = new Nullable(2); //Nullable Type - return true; } [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsBuiltInComEnabled))] [SkipOnMono("Requires COM support")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/85234", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsGCStress))] public static void Run() { IUnknownMarshalingTest testObj = new IUnknownMarshalingTest(); @@ -225,7 +129,6 @@ public static void Run() [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsBuiltInComEnabled))] [SkipOnMono("Requires COM support")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/85234", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsGCStress))] public static void RunInALC() { TestLibrary.Utilities.ExecuteAndUnload(typeof(IUnknownMarshalingTest).Assembly.Location, nameof(IUnknownMarshalingTest), nameof(Run)); diff --git a/src/tests/Interop/PInvoke/Miscellaneous/HandleRef/HandleRefTest.cs b/src/tests/Interop/PInvoke/Miscellaneous/HandleRef/HandleRefTest.cs index d553cbc12f079c..3453a75214139a 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/HandleRef/HandleRefTest.cs +++ b/src/tests/Interop/PInvoke/Miscellaneous/HandleRef/HandleRefTest.cs @@ -72,8 +72,6 @@ public static void Validate_InvalidReturn() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] - [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/96364", RuntimeTestModes.GCStress3)] - [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/96364", RuntimeTestModes.GCStressC)] public static void Validate_NoGC() { HandleRef hr = CreateHandleRef(); From bd84a96ba5ad9e4eb8bff8ed4b93de2d88b1533f Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Sat, 20 Jan 2024 18:23:12 -0800 Subject: [PATCH 149/189] Add public APIs for persisted AssemblyBulder (#97177) * Add public APIs for persisted AssemblyBuilder * Update tests to use public API, refactor tests so that the temp files deleted * Make AB.SaveCore(Stream) virtual and add meaningful message for excepiton thrown Co-authored-by: Stephen Toub Co-authored-by: Jan Kotas * Remove unneeded message * Update newer tests to use new public APIs --------- Co-authored-by: Stephen Toub Co-authored-by: Jan Kotas --- .../src/Resources/Strings.resx | 5 +- .../System/Reflection/Emit/AssemblyBuilder.cs | 49 + .../ref/System.Reflection.Emit.cs | 4 + .../ILLink.Descriptors.LibraryBuild.xml | 5 +- .../Reflection/Emit/AssemblyBuilderImpl.cs | 24 +- .../AssemblySaveAssemblyBuilder.cs | 10 +- .../AssemblySaveConstructorBuilderTests.cs | 49 +- .../AssemblySaveCustomAttributeTests.cs | 426 +++--- .../AssemblySaveEnumBuilderTests.cs | 87 +- .../AssemblySaveEventBuilderTests.cs | 39 +- .../AssemblySaveILGeneratorTests.cs | 1156 +++++++++-------- .../AssemblySaveModuleBuilderTests.cs | 24 +- .../AssemblySavePropertyBuilderTests.cs | 110 +- .../AssemblySaveTools.cs | 38 +- .../AssemblySaveTypeBuilderAPIsTests.cs | 97 +- .../AssemblySaveTypeBuilderTests.cs | 400 +++--- .../RegexAssemblyCompiler.cs | 13 +- ...iCompatBaseline.NetCoreAppLatestStable.xml | 2 +- 18 files changed, 1357 insertions(+), 1181 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index d2bc611dbbbee9..f32b66df95e796 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4289,4 +4289,7 @@ This operation is not available because the reflection support was disabled at compile time. - + + This AssemblyBuilder instance doesn't support saving. Use AssemblyBuilder.DefinePersistedAssembly to create an AssemblyBuilder instance that supports saving. + + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs index d7ee53669cc91b..8599d39aa4b166 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.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.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; @@ -32,8 +33,56 @@ public ModuleBuilder DefineDynamicModule(string name) return GetDynamicModuleCore(name); } + /// + /// Defines an that can be saved to a file or stream. + /// + /// The name of the assembly. + /// The assembly that denotes the "system assembly" that houses the well-known types such as + /// A collection that contains the attributes of the assembly. + /// An that can be persisted. + /// The or or is null. + /// Currently the persisted assembly doesn't support running, need to save it and load back to run. + public static AssemblyBuilder DefinePersistedAssembly(AssemblyName name, Assembly coreAssembly, IEnumerable? assemblyAttributes = null) + { + ArgumentNullException.ThrowIfNull(name); + ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name"); + ArgumentNullException.ThrowIfNull(coreAssembly); + + Type assemblyType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!; + ConstructorInfo con = assemblyType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, [typeof(AssemblyName), typeof(Assembly), typeof(IEnumerable)])!; + return (AssemblyBuilder)con.Invoke([name, coreAssembly, assemblyAttributes]); + } + protected abstract ModuleBuilder? GetDynamicModuleCore(string name); + /// + /// Serializes the assembly to . + /// + /// The to which the assembly serialized. + /// is null. + /// The AssemblyBuilder instance doesn't support saving. + public void Save(Stream stream) => SaveCore(stream); + + /// + /// Saves the assembly to disk. + /// + /// The file name of the assembly. + /// is null. + /// The AssemblyBuilder instance doesn't support saving. + public void Save(string assemblyFileName) + { + ArgumentNullException.ThrowIfNull(assemblyFileName); + + using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write); + SaveCore(peStream); + } + + /// + /// When implemented in a derived type, serializes the assembly to a stream. + /// + /// The stream to which the assembly serialized. + protected virtual void SaveCore(Stream stream) => throw new NotSupportedException(SR.NotSupported_AssemblySave); + public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute) { ArgumentNullException.ThrowIfNull(con); diff --git a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs index 7d55329ba942f3..1cd9aa71da7432 100644 --- a/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs +++ b/src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs @@ -24,6 +24,7 @@ protected AssemblyBuilder() { } public static System.Reflection.Emit.AssemblyBuilder DefineDynamicAssembly(System.Reflection.AssemblyName name, System.Reflection.Emit.AssemblyBuilderAccess access) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")] public static System.Reflection.Emit.AssemblyBuilder DefineDynamicAssembly(System.Reflection.AssemblyName name, System.Reflection.Emit.AssemblyBuilderAccess access, System.Collections.Generic.IEnumerable? assemblyAttributes) { throw null; } + public static System.Reflection.Emit.AssemblyBuilder DefinePersistedAssembly(System.Reflection.AssemblyName name, System.Reflection.Assembly coreAssembly, System.Collections.Generic.IEnumerable? assemblyAttributes = null) { throw null; } public System.Reflection.Emit.ModuleBuilder DefineDynamicModule(string name) { throw null; } protected abstract System.Reflection.Emit.ModuleBuilder DefineDynamicModuleCore(string name); public override bool Equals(object? obj) { throw null; } @@ -54,6 +55,9 @@ protected AssemblyBuilder() { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed by trimming. If the type name is a string literal, consider using Type.GetType instead.")] public override System.Type? GetType(string name, bool throwOnError, bool ignoreCase) { throw null; } public override bool IsDefined(System.Type attributeType, bool inherit) { throw null; } + public void Save(string assemblyFileName) { throw null; } + public void Save(System.IO.Stream stream) { throw null; } + protected virtual void SaveCore(System.IO.Stream stream) { } public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { } public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { } protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan binaryAttribute); diff --git a/src/libraries/System.Reflection.Emit/src/ILLink/ILLink.Descriptors.LibraryBuild.xml b/src/libraries/System.Reflection.Emit/src/ILLink/ILLink.Descriptors.LibraryBuild.xml index 4ac5a6f3584506..8cf11ab4c01f72 100644 --- a/src/libraries/System.Reflection.Emit/src/ILLink/ILLink.Descriptors.LibraryBuild.xml +++ b/src/libraries/System.Reflection.Emit/src/ILLink/ILLink.Descriptors.LibraryBuild.xml @@ -1,9 +1,8 @@ - - - + + diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs index 834d8612a949e0..b52bec9f11c4a8 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs @@ -19,15 +19,9 @@ internal sealed class AssemblyBuilderImpl : AssemblyBuilder internal List? _customAttributes; - internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable? assemblyAttributes) + internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable? assemblyAttributes = null) { - ArgumentNullException.ThrowIfNull(name); - - name = (AssemblyName)name.Clone(); - - ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name"); - - _assemblyName = name; + _assemblyName = (AssemblyName)name.Clone(); _coreAssembly = coreAssembly; _metadataBuilder = new MetadataBuilder(); @@ -40,10 +34,6 @@ internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerab } } - internal static AssemblyBuilderImpl DefinePersistedAssembly(AssemblyName name, Assembly coreAssembly, - IEnumerable? assemblyAttributes) - => new AssemblyBuilderImpl(name, coreAssembly, assemblyAttributes); - private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fieldData) { var peHeaderBuilder = new PEHeaderBuilder( @@ -64,7 +54,7 @@ private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fi peBlob.WriteContentTo(peStream); } - internal void Save(Stream stream) + protected override void SaveCore(Stream stream) { ArgumentNullException.ThrowIfNull(stream); @@ -103,14 +93,6 @@ internal void Save(Stream stream) private static AssemblyFlags AddContentType(AssemblyFlags flags, AssemblyContentType contentType) => (AssemblyFlags)((int)contentType << 9) | flags; - internal void Save(string assemblyFileName) - { - ArgumentNullException.ThrowIfNull(assemblyFileName); - - using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write); - Save(peStream); - } - protected override ModuleBuilder DefineDynamicModuleCore(string name) { if (_module != null) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs index 4fca521e6a352c..5dbabe683e0734 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs @@ -34,7 +34,7 @@ public void AssemblyWithDifferentTypes() aName.CultureInfo = new CultureInfo("en"); aName.Flags = AssemblyNameFlags.Retargetable; - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(aName, null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(aName); ab.SetCustomAttribute(new CustomAttributeBuilder(typeof(AssemblyDelaySignAttribute).GetConstructor([typeof(bool)]), [true])); @@ -213,10 +213,12 @@ public void AssemblyWithDifferentTypes() eventb.SetRemoveOnMethod(mbRemove); tbEvents.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - CheckAssembly(assemblyFromDisk); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + CheckAssembly(mlc.LoadFromAssemblyPath(file.Path)); + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs index 31ec0e31d87be0..618b01b2a9ae06 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveConstructorBuilderTests.cs @@ -15,7 +15,7 @@ public void DefineConstructorsTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); ConstructorBuilder constructor = type.DefineDefaultConstructor(MethodAttributes.Public); ConstructorBuilder constructor2 = type.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, [typeof(int)]); constructor2.DefineParameter(1, ParameterAttributes.None, "parameter1"); @@ -28,7 +28,7 @@ public void DefineConstructorsTest() il.Emit(OpCodes.Stfld, fieldBuilderA); il.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { @@ -51,7 +51,7 @@ public void DefineConstructorsTest() [Fact] public void DefineDefaultConstructor_WithTypeBuilderParent() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.CreateType(); TypeBuilder child = ab.GetDynamicModule("MyModule").DefineType("ChildType", TypeAttributes.Public | TypeAttributes.Class); child.SetParent(type); @@ -69,7 +69,7 @@ public void DefineDefaultConstructor_TypesWithGenericParents() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.DefineGenericParameters("T"); ConstructorBuilder constructor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); FieldBuilder field = type.DefineField("TestField", typeof(bool), FieldAttributes.Public | FieldAttributes.Static); @@ -93,23 +93,26 @@ public void DefineDefaultConstructor_TypesWithGenericParents() type2.SetParent(genericList); type2.DefineDefaultConstructor(MethodAttributes.Public); type2.CreateTypeInfo(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - ConstructorInfo[] ctors = assemblyFromDisk.GetType("MyType").GetConstructors(); - Assert.Equal(1, ctors.Length); - Assert.Empty(ctors[0].GetParameters()); - Type derivedFromFile = assemblyFromDisk.GetType("Derived"); - Assert.NotNull(derivedFromFile.GetConstructor(Type.EmptyTypes)); - Assert.Equal(genericParent.FullName, derivedFromFile.BaseType.FullName); - Assert.NotNull(assemblyFromDisk.GetType("Type2").GetConstructor(Type.EmptyTypes)); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + ConstructorInfo[] ctors = assemblyFromDisk.GetType("MyType").GetConstructors(); + Assert.Equal(1, ctors.Length); + Assert.Empty(ctors[0].GetParameters()); + Type derivedFromFile = assemblyFromDisk.GetType("Derived"); + Assert.NotNull(derivedFromFile.GetConstructor(Type.EmptyTypes)); + Assert.Equal(genericParent.FullName, derivedFromFile.BaseType.FullName); + Assert.NotNull(assemblyFromDisk.GetType("Type2").GetConstructor(Type.EmptyTypes)); + } } } [Fact] public void DefineDefaultConstructor_Interface_ThrowsInvalidOperationException() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), null, typeof(string), out var _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); TypeBuilder type = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract); Assert.Throws(() => type.DefineDefaultConstructor(MethodAttributes.Public)); } @@ -117,7 +120,7 @@ public void DefineDefaultConstructor_Interface_ThrowsInvalidOperationException() [Fact] public void DefineDefaultConstructor_ThrowsNotSupportedException_IfParentNotCreated() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); TypeBuilder child = ab.GetDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public); child.SetParent(type); Assert.Throws(() => child.DefineDefaultConstructor(MethodAttributes.Public)); @@ -126,14 +129,14 @@ public void DefineDefaultConstructor_ThrowsNotSupportedException_IfParentNotCrea [Fact] public void DefineDefaultConstructor_StaticVirtual_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); AssertExtensions.Throws(null, () => type.DefineDefaultConstructor(MethodAttributes.Virtual | MethodAttributes.Static)); } [Fact] public void DefineDefaultConstructor_ParentNoDefaultConstructor_ThrowsNotSupportedException() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); FieldBuilder field = type.DefineField("TestField", typeof(int), FieldAttributes.Family); ConstructorBuilder constructor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new[] { typeof(int) }); @@ -156,7 +159,7 @@ public void DefineDefaultConstructor_ParentNoDefaultConstructor_ThrowsNotSupport [InlineData(MethodAttributes.PrivateScope)] public void DefineDefaultConstructor_ParentPrivateDefaultConstructor_ThrowsNotSupportedException(MethodAttributes attributes) { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder baseType, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder baseType); ConstructorBuilder constructor = baseType.DefineConstructor(attributes, CallingConventions.HasThis, new[] { typeof(int) }); constructor.GetILGenerator().Emit(OpCodes.Ret); @@ -168,7 +171,7 @@ public void DefineDefaultConstructor_ParentPrivateDefaultConstructor_ThrowsNotSu [Fact] public void GetConstructor_DeclaringTypeOfConstructorGenericTypeDefinition() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.DefineGenericParameters("T"); ConstructorBuilder ctor = type.DefineDefaultConstructor(MethodAttributes.PrivateScope | MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName); @@ -179,7 +182,7 @@ public void GetConstructor_DeclaringTypeOfConstructorGenericTypeDefinition() [Fact] public void TypeBuilder_GetConstructorWorks() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.DefineGenericParameters("T"); ConstructorBuilder ctor = type.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName); @@ -193,7 +196,7 @@ public void TypeBuilder_GetConstructorWorks() [Fact] public void GetConstructor_DeclaringTypeOfConstructorNotGenericTypeDefinitionOfType_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type1, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type1); type1.DefineGenericParameters("T"); TypeBuilder type2 = ((ModuleBuilder)type1.Module).DefineType("TestType2", TypeAttributes.Class | TypeAttributes.Public); @@ -209,7 +212,7 @@ public void GetConstructor_DeclaringTypeOfConstructorNotGenericTypeDefinitionOfT [Fact] public void GetConstructor_TypeNotGeneric_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); ConstructorBuilder ctor = type.DefineDefaultConstructor(MethodAttributes.Public); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs index 7d7191c8c365a0..e2f51e70dab228 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveCustomAttributeTests.cs @@ -42,12 +42,15 @@ public void AssemblyModuleWithCustomAttributes() { WriteAssemblyToDisk(assemblyName, Type.EmptyTypes, file.Path, _attributes, _attributes); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Module moduleFromDisk = assemblyFromDisk.Modules.First(); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); - AssemblySaveTools.AssertAssemblyNameAndModule(assemblyName, assemblyFromDisk.GetName(), moduleFromDisk); - ValidateAttributes(assemblyFromDisk.GetCustomAttributesData()); - ValidateAttributes(moduleFromDisk.GetCustomAttributesData()); + AssemblySaveTools.AssertAssemblyNameAndModule(assemblyName, assemblyFromDisk.GetName(), moduleFromDisk); + ValidateAttributes(assemblyFromDisk.GetCustomAttributesData()); + ValidateAttributes(moduleFromDisk.GetCustomAttributesData()); + } } } @@ -61,33 +64,36 @@ public void MethodFieldWithCustomAttributes() WriteAssemblyToDisk(PopulateAssemblyName(), types, file.Path, typeAttributes: _attributes, methodAttributes: _attributes, fieldAttributes: _attributes); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); - Module moduleFromDisk = assemblyFromDisk.Modules.First(); - Type[] typesFromDisk = moduleFromDisk.GetTypes(); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); + Type[] typesFromDisk = moduleFromDisk.GetTypes(); - Assert.Equal(types.Length, typesFromDisk.Length); + Assert.Equal(types.Length, typesFromDisk.Length); - for (int i = 0; i < types.Length; i++) - { - Type typeFromDisk = typesFromDisk[i]; - Type sourceType = types[i]; - MethodInfo[] methodsFromDisk = typeFromDisk.IsValueType ? typeFromDisk.GetMethods(BindingFlags.DeclaredOnly) : typeFromDisk.GetMethods(); - FieldInfo[] fieldsFromDisk = typeFromDisk.GetFields(); + for (int i = 0; i < types.Length; i++) + { + Type typeFromDisk = typesFromDisk[i]; + Type sourceType = types[i]; + MethodInfo[] methodsFromDisk = typeFromDisk.IsValueType ? typeFromDisk.GetMethods(BindingFlags.DeclaredOnly) : typeFromDisk.GetMethods(); + FieldInfo[] fieldsFromDisk = typeFromDisk.GetFields(); - AssemblySaveTools.AssertTypeProperties(sourceType, typeFromDisk); - AssemblySaveTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); - AssemblySaveTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); - ValidateAttributes(typeFromDisk.GetCustomAttributesData()); + AssemblySaveTools.AssertTypeProperties(sourceType, typeFromDisk); + AssemblySaveTools.AssertMethods(sourceType.IsValueType ? sourceType.GetMethods(BindingFlags.DeclaredOnly) : sourceType.GetMethods(), methodsFromDisk); + AssemblySaveTools.AssertFields(sourceType.GetFields(), fieldsFromDisk); + ValidateAttributes(typeFromDisk.GetCustomAttributesData()); - for (int j = 0; j < methodsFromDisk.Length; j++) - { - ValidateAttributes(methodsFromDisk[j].GetCustomAttributesData()); - } + for (int j = 0; j < methodsFromDisk.Length; j++) + { + ValidateAttributes(methodsFromDisk[j].GetCustomAttributesData()); + } - for (int j = 0; j < fieldsFromDisk.Length; j++) - { - ValidateAttributes(fieldsFromDisk[j].GetCustomAttributesData()); + for (int j = 0; j < fieldsFromDisk.Length; j++) + { + ValidateAttributes(fieldsFromDisk[j].GetCustomAttributesData()); + } } } } @@ -119,10 +125,10 @@ private static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, List? moduleAttributes = null, List? typeAttributes = null, List? methodAttributes = null, List? fieldAttributes = null) { - AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(assemblyName, assemblyAttributes, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(assemblyName, assemblyAttributes); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name); PopulateMembersForModule(mb, types, moduleAttributes, typeAttributes, methodAttributes, fieldAttributes); - saveMethod.Invoke(assemblyBuilder, new object[] { fileLocation }); + assemblyBuilder.Save(fileLocation); } private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types, List? moduleAttributes, @@ -192,62 +198,64 @@ public void CreateStructWithPseudoCustomAttributesTest() new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), new object[] { }) }; - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( - PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(PopulateAssemblyName()); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes, type.BaseType); DefineFieldsAndSetAttributes(fieldAttributes.ToList(), type.GetFields(), tb); typeAttributes.ForEach(tb.SetCustomAttribute); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Module moduleFromDisk = assemblyFromDisk.Modules.First(); - Type testType = moduleFromDisk.GetTypes()[0]; - IList attributesFromDisk = testType.GetCustomAttributesData(); - - Assert.Equal(typeAttributes.Count - 3, attributesFromDisk.Count); // 3 pseudo attributes - Assert.True((testType.Attributes & TypeAttributes.Serializable) != 0); // SerializableAttribute - Assert.True((testType.Attributes & TypeAttributes.SpecialName) != 0); // SpecialNameAttribute - Assert.True((testType.Attributes & TypeAttributes.ExplicitLayout) != 0); // StructLayoutAttribute - Assert.True((testType.Attributes & TypeAttributes.UnicodeClass) != 0); // StructLayoutAttribute, not sure if we could test the PackingSize and Size - - for (int i = 0; i < attributesFromDisk.Count; i++) + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { - switch (attributesFromDisk[i].AttributeType.Name) + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); + Type testType = moduleFromDisk.GetTypes()[0]; + IList attributesFromDisk = testType.GetCustomAttributesData(); + + Assert.Equal(typeAttributes.Count - 3, attributesFromDisk.Count); // 3 pseudo attributes + Assert.True((testType.Attributes & TypeAttributes.Serializable) != 0); // SerializableAttribute + Assert.True((testType.Attributes & TypeAttributes.SpecialName) != 0); // SpecialNameAttribute + Assert.True((testType.Attributes & TypeAttributes.ExplicitLayout) != 0); // StructLayoutAttribute + Assert.True((testType.Attributes & TypeAttributes.UnicodeClass) != 0); // StructLayoutAttribute, not sure if we could test the PackingSize and Size + + for (int i = 0; i < attributesFromDisk.Count; i++) { - case "GuidAttribute": - Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value); - break; - default: - Assert.Fail($"Not expected attribute : {attributesFromDisk[i].AttributeType.Name}"); - break; + switch (attributesFromDisk[i].AttributeType.Name) + { + case "GuidAttribute": + Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value); + break; + default: + Assert.Fail($"Not expected attribute : {attributesFromDisk[i].AttributeType.Name}"); + break; + } } - } - FieldInfo field = testType.GetFields()[0]; - IList fieldAttributesFromDisk = field.GetCustomAttributesData(); + FieldInfo field = testType.GetFields()[0]; + IList fieldAttributesFromDisk = field.GetCustomAttributesData(); - Assert.Equal(3, fieldAttributesFromDisk.Count); - Assert.True((field.Attributes & FieldAttributes.NotSerialized) != 0); // NonSerializedAttribute - Assert.True((field.Attributes & FieldAttributes.SpecialName) != 0); // SpecialNameAttribute - Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute + Assert.Equal(3, fieldAttributesFromDisk.Count); + Assert.True((field.Attributes & FieldAttributes.NotSerialized) != 0); // NonSerializedAttribute + Assert.True((field.Attributes & FieldAttributes.SpecialName) != 0); // SpecialNameAttribute + Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute - for (int i = 0; i < fieldAttributesFromDisk.Count; i++) - { - switch (fieldAttributesFromDisk[i].AttributeType.Name) + for (int i = 0; i < fieldAttributesFromDisk.Count; i++) { - case "FieldOffsetAttribute": - Assert.Equal(2, fieldAttributesFromDisk[i].ConstructorArguments[0].Value); - break; - case "MarshalAsAttribute": - Assert.Equal(UnmanagedType.I4, (UnmanagedType)fieldAttributesFromDisk[i].ConstructorArguments[0].Value); - break; - case "GuidAttribute": - Assert.Equal(s_guidPair.args[0], fieldAttributesFromDisk[i].ConstructorArguments[0].Value); - break; - default: - Assert.Fail($"Not expected attribute : {fieldAttributesFromDisk[i].AttributeType.Name}"); - break; + switch (fieldAttributesFromDisk[i].AttributeType.Name) + { + case "FieldOffsetAttribute": + Assert.Equal(2, fieldAttributesFromDisk[i].ConstructorArguments[0].Value); + break; + case "MarshalAsAttribute": + Assert.Equal(UnmanagedType.I4, (UnmanagedType)fieldAttributesFromDisk[i].ConstructorArguments[0].Value); + break; + case "GuidAttribute": + Assert.Equal(s_guidPair.args[0], fieldAttributesFromDisk[i].ConstructorArguments[0].Value); + break; + default: + Assert.Fail($"Not expected attribute : {fieldAttributesFromDisk[i].AttributeType.Name}"); + break; + } } } } @@ -282,128 +290,131 @@ public void InterfacesWithPseudoCustomAttributes() new CustomAttributeBuilder(marshalAsEnumCtor, new object[] { UnmanagedType.CustomMarshaler }, new FieldInfo[] { typeof(MarshalAsAttribute).GetField("MarshalType")}, new object[] { typeof(EmptyTestClass).AssemblyQualifiedName })}; - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(PopulateAssemblyName()); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes); typeAttributes.ForEach(tb.SetCustomAttribute); DefineMethodsAndSetAttributes(methodAttributes, tb, type.GetMethods(), parameterAttributes); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; - IList attributesFromDisk = testType.GetCustomAttributesData(); - - Assert.Equal(typeAttributes.Count, attributesFromDisk.Count); - Assert.True((testType.Attributes & TypeAttributes.Import) != 0); // ComImportAttribute - Assert.True((testType.Attributes & TypeAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute - for (int i = 0; i < attributesFromDisk.Count; i++) - { - switch (attributesFromDisk[i].AttributeType.Name) - { - case "ComImportAttribute": // just making sure that these attributes are expected - case "SuppressUnmanagedCodeSecurityAttribute": - break; - case "GuidAttribute": - Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value); - break; - default: - Assert.Fail($"Not expected attribute : {attributesFromDisk[i].AttributeType.Name}"); - break; - } - } - - foreach (var method in testType.GetMethods()) + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { - IList methodAttributesFromDisk = method.GetCustomAttributesData(); - - Assert.True((method.Attributes & MethodAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute - Assert.True((method.Attributes & MethodAttributes.SpecialName) != 0); // SpecialNameAttribute - MethodImplAttributes methodImpl = method.GetMethodImplementationFlags(); - Assert.True((methodImpl & MethodImplAttributes.NoInlining) != 0); // MethodImplAttribute - Assert.True((methodImpl & MethodImplAttributes.AggressiveOptimization) != 0); // MethodImplAttribute - Assert.True((methodImpl & MethodImplAttributes.PreserveSig) != 0); // PreserveSigAttribute - Assert.Equal(methodAttributes.Count - 2, methodAttributesFromDisk.Count); - - for (int i = 0; i < methodAttributesFromDisk.Count; i++) + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; + IList attributesFromDisk = testType.GetCustomAttributesData(); + + Assert.Equal(typeAttributes.Count, attributesFromDisk.Count); + Assert.True((testType.Attributes & TypeAttributes.Import) != 0); // ComImportAttribute + Assert.True((testType.Attributes & TypeAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute + for (int i = 0; i < attributesFromDisk.Count; i++) { - switch (methodAttributesFromDisk[i].AttributeType.Name) + switch (attributesFromDisk[i].AttributeType.Name) { + case "ComImportAttribute": // just making sure that these attributes are expected case "SuppressUnmanagedCodeSecurityAttribute": - case "PreserveSigAttribute": break; case "GuidAttribute": - Assert.Equal(s_guidPair.args[0], methodAttributesFromDisk[i].ConstructorArguments[0].Value); - break; - case "DllImportAttribute": - { - CustomAttributeData attribute = methodAttributesFromDisk[i]; - Assert.Equal("test.dll", attribute.ConstructorArguments[0].Value); - - for (int j = 0; j < attribute.NamedArguments.Count; j++) - { - switch (attribute.NamedArguments[j].MemberName) - { - case "CharSet": - Assert.Equal(CharSet.Ansi, (CharSet)attribute.NamedArguments[j].TypedValue.Value); - break; - case "SetLastError": - Assert.True((bool)attribute.NamedArguments[j].TypedValue.Value); - break; - case "CallingConvention": - Assert.Equal(CallingConvention.FastCall, (CallingConvention)attribute.NamedArguments[j].TypedValue.Value); - break; - case "BestFitMapping": - Assert.True((bool)attribute.NamedArguments[j].TypedValue.Value); - break; - case "ThrowOnUnmappableChar": - Assert.False((bool)attribute.NamedArguments[j].TypedValue.Value); - break; - } - } - } + Assert.Equal(s_guidPair.args[0], attributesFromDisk[i].ConstructorArguments[0].Value); break; default: - Assert.Fail($"Not expected attribute : {methodAttributesFromDisk[i].AttributeType.Name}"); + Assert.Fail($"Not expected attribute : {attributesFromDisk[i].AttributeType.Name}"); break; } } - foreach(ParameterInfo param in method.GetParameters()) + foreach (var method in testType.GetMethods()) { - IList paramAttributes = param.GetCustomAttributesData(); - - Assert.Equal(5, paramAttributes.Count); - Assert.True((param.Attributes & ParameterAttributes.In) != 0); // InAttribute - Assert.True((param.Attributes & ParameterAttributes.Out) != 0); // OutAttribute - Assert.True((param.Attributes & ParameterAttributes.Optional) != 0); // OptionalAttribute - Assert.True((param.Attributes & ParameterAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute + IList methodAttributesFromDisk = method.GetCustomAttributesData(); - if (param.ParameterType.Equals(typeof(string))) - { - Assert.Equal("Hello", param.DefaultValue); - } + Assert.True((method.Attributes & MethodAttributes.HasSecurity) != 0); // SuppressUnmanagedCodeSecurityAttribute + Assert.True((method.Attributes & MethodAttributes.SpecialName) != 0); // SpecialNameAttribute + MethodImplAttributes methodImpl = method.GetMethodImplementationFlags(); + Assert.True((methodImpl & MethodImplAttributes.NoInlining) != 0); // MethodImplAttribute + Assert.True((methodImpl & MethodImplAttributes.AggressiveOptimization) != 0); // MethodImplAttribute + Assert.True((methodImpl & MethodImplAttributes.PreserveSig) != 0); // PreserveSigAttribute + Assert.Equal(methodAttributes.Count - 2, methodAttributesFromDisk.Count); - for (int i = 0; i < paramAttributes.Count; i++) + for (int i = 0; i < methodAttributesFromDisk.Count; i++) { - switch (paramAttributes[i].AttributeType.Name) + switch (methodAttributesFromDisk[i].AttributeType.Name) { - case "InAttribute": - case "OutAttribute": - case "OptionalAttribute": - break; - case "MarshalAsAttribute": - Assert.Equal(UnmanagedType.CustomMarshaler, (UnmanagedType)paramAttributes[i].ConstructorArguments[0].Value); - Assert.Equal(typeof(EmptyTestClass).AssemblyQualifiedName, - paramAttributes[i].NamedArguments.First(na => na.MemberName == "MarshalType").TypedValue.Value); + case "SuppressUnmanagedCodeSecurityAttribute": + case "PreserveSigAttribute": break; case "GuidAttribute": - Assert.Equal(s_guidPair.args[0], paramAttributes[i].ConstructorArguments[0].Value); + Assert.Equal(s_guidPair.args[0], methodAttributesFromDisk[i].ConstructorArguments[0].Value); + break; + case "DllImportAttribute": + { + CustomAttributeData attribute = methodAttributesFromDisk[i]; + Assert.Equal("test.dll", attribute.ConstructorArguments[0].Value); + + for (int j = 0; j < attribute.NamedArguments.Count; j++) + { + switch (attribute.NamedArguments[j].MemberName) + { + case "CharSet": + Assert.Equal(CharSet.Ansi, (CharSet)attribute.NamedArguments[j].TypedValue.Value); + break; + case "SetLastError": + Assert.True((bool)attribute.NamedArguments[j].TypedValue.Value); + break; + case "CallingConvention": + Assert.Equal(CallingConvention.FastCall, (CallingConvention)attribute.NamedArguments[j].TypedValue.Value); + break; + case "BestFitMapping": + Assert.True((bool)attribute.NamedArguments[j].TypedValue.Value); + break; + case "ThrowOnUnmappableChar": + Assert.False((bool)attribute.NamedArguments[j].TypedValue.Value); + break; + } + } + } break; default: - Assert.Fail($"Not expected attribute : {paramAttributes[i].AttributeType.Name}"); + Assert.Fail($"Not expected attribute : {methodAttributesFromDisk[i].AttributeType.Name}"); break; } } + + foreach (ParameterInfo param in method.GetParameters()) + { + IList paramAttributes = param.GetCustomAttributesData(); + + Assert.Equal(5, paramAttributes.Count); + Assert.True((param.Attributes & ParameterAttributes.In) != 0); // InAttribute + Assert.True((param.Attributes & ParameterAttributes.Out) != 0); // OutAttribute + Assert.True((param.Attributes & ParameterAttributes.Optional) != 0); // OptionalAttribute + Assert.True((param.Attributes & ParameterAttributes.HasFieldMarshal) != 0); // MarshalAsAttribute + + if (param.ParameterType.Equals(typeof(string))) + { + Assert.Equal("Hello", param.DefaultValue); + } + + for (int i = 0; i < paramAttributes.Count; i++) + { + switch (paramAttributes[i].AttributeType.Name) + { + case "InAttribute": + case "OutAttribute": + case "OptionalAttribute": + break; + case "MarshalAsAttribute": + Assert.Equal(UnmanagedType.CustomMarshaler, (UnmanagedType)paramAttributes[i].ConstructorArguments[0].Value); + Assert.Equal(typeof(EmptyTestClass).AssemblyQualifiedName, + paramAttributes[i].NamedArguments.First(na => na.MemberName == "MarshalType").TypedValue.Value); + break; + case "GuidAttribute": + Assert.Equal(s_guidPair.args[0], paramAttributes[i].ConstructorArguments[0].Value); + break; + default: + Assert.Fail($"Not expected attribute : {paramAttributes[i].AttributeType.Name}"); + break; + } + } + } } } } @@ -432,33 +443,35 @@ public void MarshalAsPseudoCustomAttributesTest(CustomAttributeBuilder attribute using (TempFile file = TempFile.Create()) { Type type = typeof(StructWithFields); - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( - PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(PopulateAssemblyName()); TypeBuilder tb = ab.DefineDynamicModule("Module").DefineType(type.FullName, type.Attributes, type.BaseType); FieldInfo stringField = type.GetFields()[1]; FieldBuilder fb = tb.DefineField(stringField.Name, stringField.FieldType, stringField.Attributes); fb.SetCustomAttribute(attribute); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - FieldInfo field = assemblyFromDisk.Modules.First().GetTypes()[0].GetFields()[0]; - CustomAttributeData attributeFromDisk = field.GetCustomAttributesData()[0]; + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + FieldInfo field = assemblyFromDisk.Modules.First().GetTypes()[0].GetFields()[0]; + CustomAttributeData attributeFromDisk = field.GetCustomAttributesData()[0]; - Assert.Equal(1, field.GetCustomAttributesData().Count); - Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0); - Assert.Equal(expectedType, (UnmanagedType)attributeFromDisk.ConstructorArguments[0].Value); + Assert.Equal(1, field.GetCustomAttributesData().Count); + Assert.True((field.Attributes & FieldAttributes.HasFieldMarshal) != 0); + Assert.Equal(expectedType, (UnmanagedType)attributeFromDisk.ConstructorArguments[0].Value); - switch (expectedType) - { - case UnmanagedType.CustomMarshaler: - Assert.Equal(typeof(EmptyTestClass).AssemblyQualifiedName, - attributeFromDisk.NamedArguments.First(na => na.MemberName == "MarshalType").TypedValue.Value); - Assert.Equal("MyCookie", attributeFromDisk.NamedArguments.First(na => na.MemberName == "MarshalCookie").TypedValue.Value); - break; - case UnmanagedType.ByValTStr: - Assert.Equal(256, attributeFromDisk.NamedArguments.First(na => na.MemberName == "SizeConst").TypedValue.Value); - break; + switch (expectedType) + { + case UnmanagedType.CustomMarshaler: + Assert.Equal(typeof(EmptyTestClass).AssemblyQualifiedName, + attributeFromDisk.NamedArguments.First(na => na.MemberName == "MarshalType").TypedValue.Value); + Assert.Equal("MyCookie", attributeFromDisk.NamedArguments.First(na => na.MemberName == "MarshalCookie").TypedValue.Value); + break; + case UnmanagedType.ByValTStr: + Assert.Equal(256, attributeFromDisk.NamedArguments.First(na => na.MemberName == "SizeConst").TypedValue.Value); + break; + } } } } @@ -468,40 +481,43 @@ public void EnumBuilderSetCustomAttributesTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( - PopulateAssemblyName(), null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(PopulateAssemblyName()); EnumBuilder enumBuilder = ab.DefineDynamicModule("Module").DefineEnum("TestEnum", TypeAttributes.Public, typeof(int)); ConstructorInfo attributeConstructor = typeof(BoolAttribute).GetConstructor(new Type[] { typeof(bool) }); - CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeConstructor, new object[] { true }); + CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeConstructor, [true]); enumBuilder.SetCustomAttribute(attributeBuilder); enumBuilder.SetCustomAttribute(new CustomAttributeBuilder(s_guidPair.con, s_guidPair.args)); enumBuilder.CreateTypeInfo(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); - Type testEnum = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestEnum"); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testEnum = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetType("TestEnum"); - Assert.True(testEnum.IsEnum); - AssemblySaveTools.AssertTypeProperties(enumBuilder, testEnum); + Assert.True(testEnum.IsEnum); + AssemblySaveTools.AssertTypeProperties(enumBuilder, testEnum); - CustomAttributeData[] attributes = testEnum.GetCustomAttributesData().ToArray(); - if (attributes[0].AttributeType.Name == s_guidType.Name) - { - AssertEnumAttributes(s_guidType.FullName, "9ED54F84-A89D-4fcd-A854-44251E925F09", attributes[0]); - AssertEnumAttributes(typeof(BoolAttribute).FullName, true, attributes[1]); - } - else - { - AssertEnumAttributes(s_guidType.FullName, "9ED54F84-A89D-4fcd-A854-44251E925F09", attributes[1]); - AssertEnumAttributes(typeof(BoolAttribute).FullName, true, attributes[0]); - } + CustomAttributeData[] attributes = testEnum.GetCustomAttributesData().ToArray(); + if (attributes[0].AttributeType.Name == s_guidType.Name) + { + AssertEnumAttributes(s_guidType.FullName, "9ED54F84-A89D-4fcd-A854-44251E925F09", attributes[0]); + AssertEnumAttributes(typeof(BoolAttribute).FullName, true, attributes[1]); + } + else + { + AssertEnumAttributes(s_guidType.FullName, "9ED54F84-A89D-4fcd-A854-44251E925F09", attributes[1]); + AssertEnumAttributes(typeof(BoolAttribute).FullName, true, attributes[0]); + } + } } } - private void AssertEnumAttributes(string fullName, object value, CustomAttributeData testAttrbiute) + + private void AssertEnumAttributes(string fullName, object value, CustomAttributeData testAttribute) { - Assert.Equal(fullName, testAttrbiute.AttributeType.FullName); - Assert.Equal(value, testAttrbiute.ConstructorArguments[0].Value); + Assert.Equal(fullName, testAttribute.AttributeType.FullName); + Assert.Equal(value, testAttribute.ConstructorArguments[0].Value); } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs index 8ab0de66a8f205..9ea6560c5aea0e 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEnumBuilderTests.cs @@ -64,24 +64,27 @@ public void DefineLiteral(Type underlyingType, object literalValue) { using (TempFile file = TempFile.Create()) { - EnumBuilder enumBuilder = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod, out TypeBuilder type, underlyingType); + EnumBuilder enumBuilder = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out TypeBuilder type, underlyingType); FieldBuilder literal = enumBuilder.DefineLiteral("FieldOne", literalValue); enumBuilder.CreateTypeInfo(); type.CreateTypeInfo(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Module moduleFromDisk = assemblyFromDisk.Modules.First(); - Type testEnum = moduleFromDisk.GetType("TestEnum"); - - Assert.True(testEnum.IsEnum); - AssemblySaveTools.AssertTypeProperties(enumBuilder, testEnum); - Assert.Equal(underlyingType.FullName, testEnum.GetEnumUnderlyingType().FullName); - - FieldInfo testField = testEnum.GetField("FieldOne"); - Assert.Equal(enumBuilder.Name, testField.DeclaringType.Name); - Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault, literal.Attributes); - Assert.Equal(enumBuilder.AsType().FullName, testField.FieldType.FullName); + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); + Type testEnum = moduleFromDisk.GetType("TestEnum"); + + Assert.True(testEnum.IsEnum); + AssemblySaveTools.AssertTypeProperties(enumBuilder, testEnum); + Assert.Equal(underlyingType.FullName, testEnum.GetEnumUnderlyingType().FullName); + + FieldInfo testField = testEnum.GetField("FieldOne"); + Assert.Equal(enumBuilder.Name, testField.DeclaringType.Name); + Assert.Equal(FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault, literal.Attributes); + Assert.Equal(enumBuilder.AsType().FullName, testField.FieldType.FullName); + } } } @@ -94,7 +97,7 @@ public void SaveArrayTypeSignature(int rank, string name) { using (TempFile file = TempFile.Create()) { - EnumBuilder enumBuilder = CreateAssemblyAndDefineEnum(out AssemblyBuilder ab, out MethodInfo saveMethod, out TypeBuilder tb); + EnumBuilder enumBuilder = CreateAssemblyAndDefineEnum(out AssemblyBuilder ab, out TypeBuilder tb); Type arrayType = rank == 0 ? enumBuilder.MakeArrayType() : enumBuilder.MakeArrayType(rank); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(arrayType); @@ -102,21 +105,23 @@ public void SaveArrayTypeSignature(int rank, string name) mb.GetILGenerator().Emit(OpCodes.Ret); enumBuilder.CreateType(); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestInterface"); - MethodInfo testMethod = testType.GetMethod("TestMethod"); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetType("TestInterface"); + MethodInfo testMethod = testType.GetMethod("TestMethod"); - AssertArrayTypeSignature(rank, name, testMethod.ReturnType); - AssertArrayTypeSignature(rank, name, testMethod.GetParameters()[1].ParameterType); + AssertArrayTypeSignature(rank, name, testMethod.ReturnType); + AssertArrayTypeSignature(rank, name, testMethod.GetParameters()[1].ParameterType); + } } } private EnumBuilder CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, - out MethodInfo saveMethod, out TypeBuilder type, Type? underlyingType = null) + out TypeBuilder type, Type? underlyingType = null) { - assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( - PopulateAssemblyName(), null, typeof(string), out saveMethod); + assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(PopulateAssemblyName()); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); type = mb.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract); return mb.DefineEnum("TestEnum", TypeAttributes.Public, underlyingType == null ? typeof(int) : underlyingType); @@ -135,7 +140,7 @@ public void SaveByRefTypeSignature() { using (TempFile file = TempFile.Create()) { - EnumBuilder eb = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod, out TypeBuilder tb); + EnumBuilder eb = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out TypeBuilder tb); Type byrefType = eb.MakeByRefType(); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(byrefType); @@ -143,14 +148,17 @@ public void SaveByRefTypeSignature() mb.GetILGenerator().Emit(OpCodes.Ret); eb.CreateType(); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + assemblyBuilder.Save(file.Path); - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestInterface"); - MethodInfo testMethod = testType.GetMethod("TestMethod"); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetType("TestInterface"); + MethodInfo testMethod = testType.GetMethod("TestMethod"); - Assert.False(testMethod.GetParameters()[0].ParameterType.IsByRef); - AssertByRefType(testMethod.GetParameters()[1].ParameterType); - AssertByRefType(testMethod.ReturnType); + Assert.False(testMethod.GetParameters()[0].ParameterType.IsByRef); + AssertByRefType(testMethod.GetParameters()[1].ParameterType); + AssertByRefType(testMethod.ReturnType); + } } } @@ -165,7 +173,7 @@ public void SavePointerTypeSignature() { using (TempFile file = TempFile.Create()) { - EnumBuilder eb = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod, out TypeBuilder tb); + EnumBuilder eb = CreateAssemblyAndDefineEnum(out AssemblyBuilder assemblyBuilder, out TypeBuilder tb); Type pointerType = eb.MakePointerType(); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(pointerType); @@ -173,14 +181,17 @@ public void SavePointerTypeSignature() mb.GetILGenerator().Emit(OpCodes.Ret); eb.CreateType(); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + assemblyBuilder.Save(file.Path); - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetType("TestInterface"); - MethodInfo testMethod = testType.GetMethod("TestMethod"); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetType("TestInterface"); + MethodInfo testMethod = testType.GetMethod("TestMethod"); - Assert.False(testMethod.GetParameters()[0].ParameterType.IsPointer); - AssertPointerType(testMethod.GetParameters()[1].ParameterType); - AssertPointerType(testMethod.ReturnType); + Assert.False(testMethod.GetParameters()[0].ParameterType.IsPointer); + AssertPointerType(testMethod.GetParameters()[1].ParameterType); + AssertPointerType(testMethod.ReturnType); + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs index 0d95824b45d9ff..d7c03fa49fc2b2 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs @@ -16,7 +16,7 @@ public void DefineEventAndItsAccessors() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); EventBuilder eventType = type.DefineEvent("TestEvent", EventAttributes.SpecialName, typeof(int)); MethodBuilder addMethod = type.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.SpecialName); MethodBuilder addMethod2 = type.DefineMethod("AddMethod2", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), Type.EmptyTypes); @@ -42,29 +42,32 @@ public void DefineEventAndItsAccessors() otherILGenerator.Emit(OpCodes.Ret); eventType.AddOtherMethod(otherMethod); type.CreateType(); - saveMethod.Invoke(ab, new[] { file.Path }); + ab.Save(file.Path); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - EventInfo eventFromDisk = typeFromDisk.GetEvent("TestEvent"); - Assert.Equal(addMethod2.Name, eventFromDisk.AddMethod.Name); - Assert.Equal(raiseMethod.Name, eventFromDisk.RaiseMethod.Name); - Assert.Equal(removeMethod.Name, eventFromDisk.RemoveMethod.Name); - Assert.Equal(typeof(int).FullName, eventFromDisk.EventHandlerType.FullName); - Assert.NotNull(typeFromDisk.GetMethod("OtherMethod", BindingFlags.NonPublic | BindingFlags.Instance)); - Assert.Equal(EventAttributes.SpecialName, eventFromDisk.Attributes); - IList caData = eventFromDisk.GetCustomAttributesData(); - Assert.Equal(1, caData.Count); - Assert.Equal(typeof(IntPropertyAttribute).FullName, caData[0].AttributeType.FullName); - Assert.Equal(1, caData[0].ConstructorArguments.Count); - Assert.Equal(9, caData[0].ConstructorArguments[0].Value); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + EventInfo eventFromDisk = typeFromDisk.GetEvent("TestEvent"); + Assert.Equal(addMethod2.Name, eventFromDisk.AddMethod.Name); + Assert.Equal(raiseMethod.Name, eventFromDisk.RaiseMethod.Name); + Assert.Equal(removeMethod.Name, eventFromDisk.RemoveMethod.Name); + Assert.Equal(typeof(int).FullName, eventFromDisk.EventHandlerType.FullName); + Assert.NotNull(typeFromDisk.GetMethod("OtherMethod", BindingFlags.NonPublic | BindingFlags.Instance)); + Assert.Equal(EventAttributes.SpecialName, eventFromDisk.Attributes); + IList caData = eventFromDisk.GetCustomAttributesData(); + Assert.Equal(1, caData.Count); + Assert.Equal(typeof(IntPropertyAttribute).FullName, caData[0].AttributeType.FullName); + Assert.Equal(1, caData[0].ConstructorArguments.Count); + Assert.Equal(9, caData[0].ConstructorArguments[0].Value); + } } } [Fact] public void Set_NullValue_ThrowsArgumentNullException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); EventBuilder eventBuilder = type.DefineEvent("TestEvent", EventAttributes.None, typeof(string)); AssertExtensions.Throws("eventtype", () => type.DefineEvent("EventTypeNull", EventAttributes.None, null)); @@ -78,7 +81,7 @@ public void Set_NullValue_ThrowsArgumentNullException() [Fact] public void Set_WhenTypeAlreadyCreated_ThrowsInvalidOperationException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); EventBuilder eventBuilder = type.DefineEvent("TestEvent", EventAttributes.None, typeof(int)); MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(int), null); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index 331f8169f3afa5..b47aca11628bd3 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -17,23 +17,25 @@ public void MethodWithEmptyBody() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder methodBuilder = type.DefineMethod("EmptyMethod", MethodAttributes.Public, typeof(void), new[] { typeof(Version) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder methodBuilder = type.DefineMethod("EmptyMethod", MethodAttributes.Public, typeof(void), [typeof(Version)]); ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodInfo method = typeFromDisk.GetMethod("EmptyMethod"); - MethodBody body = method.GetMethodBody(); - Assert.Empty(body.LocalVariables); - Assert.Empty(body.ExceptionHandlingClauses); - byte[]? bodyBytes = body.GetILAsByteArray(); - Assert.NotNull(bodyBytes); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[0]); + ab.Save(file.Path); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodInfo method = typeFromDisk.GetMethod("EmptyMethod"); + MethodBody body = method.GetMethodBody(); + Assert.Empty(body.LocalVariables); + Assert.Empty(body.ExceptionHandlingClauses); + byte[]? bodyBytes = body.GetILAsByteArray(); + Assert.NotNull(bodyBytes); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[0]); + } } } @@ -44,7 +46,7 @@ public void MethodReturning_Int(int size) { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); ILGenerator ilGenerator = method.GetILGenerator(size); @@ -52,16 +54,19 @@ public void MethodReturning_Int(int size) ilGenerator.Emit(OpCodes.Ldc_I4, expectedReturn); ilGenerator.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodInfo methodFromFile = typeFromDisk.GetMethod("TestMethod"); - MethodBody body = methodFromFile.GetMethodBody(); - byte[]? bodyBytes = body.GetILAsByteArray(); - Assert.NotNull(bodyBytes); - Assert.Equal(OpCodes.Ldc_I4_5.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[1]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodInfo methodFromFile = typeFromDisk.GetMethod("TestMethod"); + MethodBody body = methodFromFile.GetMethodBody(); + byte[]? bodyBytes = body.GetILAsByteArray(); + Assert.NotNull(bodyBytes); + Assert.Equal(OpCodes.Ldc_I4_5.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[1]); + } } } @@ -72,10 +77,10 @@ public void TypeWithTwoMethod_ReferenceMethodArguments(int multiplier) { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder multiplyMethod = type.DefineMethod("MultiplyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder multiplyMethod = type.DefineMethod("MultiplyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); multiplyMethod.DefineParameter(1, ParameterAttributes.None, "myParam"); - MethodBuilder addMethod = type.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int), typeof(int) }); + MethodBuilder addMethod = type.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); addMethod.DefineParameter(1, ParameterAttributes.None, "firsParam"); addMethod.DefineParameter(2, ParameterAttributes.None, "secondParam"); @@ -91,22 +96,25 @@ public void TypeWithTwoMethod_ReferenceMethodArguments(int multiplier) addMethodIL.Emit(OpCodes.Add); addMethodIL.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? multiplyBody = typeFromDisk.GetMethod("MultiplyMethod").GetMethodBody().GetILAsByteArray(); - byte[]? addBody = typeFromDisk.GetMethod("AddMethod").GetMethodBody().GetILAsByteArray(); - - Assert.NotNull(multiplyBody); - Assert.Equal(OpCodes.Ldarg_0.Value, multiplyBody[0]); - Assert.Equal(OpCodes.Ldc_I4_S.Value, multiplyBody[1]); - Assert.Equal(multiplier, multiplyBody[2]); - Assert.Equal(OpCodes.Mul.Value, multiplyBody[3]); - Assert.NotNull(addBody); - Assert.Equal(OpCodes.Ldarg_0.Value, addBody[0]); - Assert.Equal(OpCodes.Ldarg_1.Value, addBody[1]); - Assert.Equal(OpCodes.Add.Value, addBody[2]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? multiplyBody = typeFromDisk.GetMethod("MultiplyMethod").GetMethodBody().GetILAsByteArray(); + byte[]? addBody = typeFromDisk.GetMethod("AddMethod").GetMethodBody().GetILAsByteArray(); + + Assert.NotNull(multiplyBody); + Assert.Equal(OpCodes.Ldarg_0.Value, multiplyBody[0]); + Assert.Equal(OpCodes.Ldc_I4_S.Value, multiplyBody[1]); + Assert.Equal(multiplier, multiplyBody[2]); + Assert.Equal(OpCodes.Mul.Value, multiplyBody[3]); + Assert.NotNull(addBody); + Assert.Equal(OpCodes.Ldarg_0.Value, addBody[0]); + Assert.Equal(OpCodes.Ldarg_1.Value, addBody[1]); + Assert.Equal(OpCodes.Add.Value, addBody[2]); + } } } @@ -115,9 +123,9 @@ public void MultipleTypesWithMultipleMethods() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder multiplyMethod = type.DefineMethod("MultiplyMethod", MethodAttributes.Public, typeof(short), new Type[] { typeof(short) }); - MethodBuilder addMethod = type.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(double), new Type[] { typeof(double) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder multiplyMethod = type.DefineMethod("MultiplyMethod", MethodAttributes.Public, typeof(short), [typeof(short)]); + MethodBuilder addMethod = type.DefineMethod("AddMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(double), [typeof(double)]); ILGenerator multiplyMethodIL = multiplyMethod.GetILGenerator(); multiplyMethodIL.Emit(OpCodes.Ldarg, 1); @@ -145,38 +153,41 @@ public void MultipleTypesWithMultipleMethods() longMethodIL.Emit(OpCodes.Ldc_I8, (long)1234567); longMethodIL.Emit(OpCodes.Ret); anotherType.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Module moduleFromFile = assemblyFromDisk.Modules.First(); - Type typeFromDisk = moduleFromFile.GetType("MyType"); - Type anotherTypeFromDisk = moduleFromFile.GetType("AnotherType"); - byte[]? multiplyBody = typeFromDisk.GetMethod("MultiplyMethod").GetMethodBody().GetILAsByteArray(); - byte[]? addBody = typeFromDisk.GetMethod("AddMethod").GetMethodBody().GetILAsByteArray(); - byte[]? stringBody = anotherTypeFromDisk.GetMethod("StringMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().GetILAsByteArray(); - byte[]? floatBody = anotherTypeFromDisk.GetMethod("FloatMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().GetILAsByteArray(); - byte[]? longBody = anotherTypeFromDisk.GetMethod("LongMethod", BindingFlags.NonPublic | BindingFlags.Static).GetMethodBody().GetILAsByteArray(); - - Assert.NotNull(multiplyBody); - Assert.Equal(OpCodes.Ldarg_1.Value, multiplyBody[0]); - Assert.Equal(OpCodes.Ldc_I4_S.Value, multiplyBody[1]); - Assert.Equal(11, multiplyBody[2]); - Assert.NotNull(addBody); - Assert.Equal(OpCodes.Ldarg_0.Value, addBody[0]); - Assert.Equal(OpCodes.Ldc_R8.Value, addBody[1]); - Assert.NotNull(stringBody); - Assert.Equal(OpCodes.Ldstr.Value, stringBody[0]); - Assert.NotNull(floatBody); - Assert.Equal(OpCodes.Ldc_R4.Value, floatBody[0]); - Assert.NotNull(longBody); - Assert.Equal(OpCodes.Ldc_I8.Value, longBody[0]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module moduleFromFile = assemblyFromDisk.Modules.First(); + Type typeFromDisk = moduleFromFile.GetType("MyType"); + Type anotherTypeFromDisk = moduleFromFile.GetType("AnotherType"); + byte[]? multiplyBody = typeFromDisk.GetMethod("MultiplyMethod").GetMethodBody().GetILAsByteArray(); + byte[]? addBody = typeFromDisk.GetMethod("AddMethod").GetMethodBody().GetILAsByteArray(); + byte[]? stringBody = anotherTypeFromDisk.GetMethod("StringMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().GetILAsByteArray(); + byte[]? floatBody = anotherTypeFromDisk.GetMethod("FloatMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().GetILAsByteArray(); + byte[]? longBody = anotherTypeFromDisk.GetMethod("LongMethod", BindingFlags.NonPublic | BindingFlags.Static).GetMethodBody().GetILAsByteArray(); + + Assert.NotNull(multiplyBody); + Assert.Equal(OpCodes.Ldarg_1.Value, multiplyBody[0]); + Assert.Equal(OpCodes.Ldc_I4_S.Value, multiplyBody[1]); + Assert.Equal(11, multiplyBody[2]); + Assert.NotNull(addBody); + Assert.Equal(OpCodes.Ldarg_0.Value, addBody[0]); + Assert.Equal(OpCodes.Ldc_R8.Value, addBody[1]); + Assert.NotNull(stringBody); + Assert.Equal(OpCodes.Ldstr.Value, stringBody[0]); + Assert.NotNull(floatBody); + Assert.Equal(OpCodes.Ldc_R4.Value, floatBody[0]); + Assert.NotNull(longBody); + Assert.Equal(OpCodes.Ldc_I8.Value, longBody[0]); + } } } [Fact] public void ILOffset_Test() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("Method1", MethodAttributes.Public | MethodAttributes.Static, typeof(Type), Type.EmptyTypes); ILGenerator ilGenerator = method.GetILGenerator(); @@ -190,8 +201,8 @@ public void ILMaxStack_Test() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), new[] { typeof(int), typeof(long), typeof(short), typeof(byte) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), [typeof(int), typeof(long), typeof(short), typeof(byte)]); ILGenerator il1 = method1.GetILGenerator(); // public int Method1(int x, int y, short z, byte r) => @@ -217,7 +228,7 @@ public void ILMaxStack_Test() il1.Emit(OpCodes.Add); // pop 2 push 1 stack size 1 il1.Emit(OpCodes.Ret); // pop 1 stack size 0 - MethodBuilder method2 = type.DefineMethod("Method2", MethodAttributes.Public, typeof(int), new Type[] { typeof(int), typeof(byte) }); + MethodBuilder method2 = type.DefineMethod("Method2", MethodAttributes.Public, typeof(int), [typeof(int), typeof(byte)]); ILGenerator il2 = method2.GetILGenerator(); // int Method2(int x, int y) => x + (y + 18); @@ -228,18 +239,21 @@ public void ILMaxStack_Test() il2.Emit(OpCodes.Add); // pop 2 push 1 stack size 1 il2.Emit(OpCodes.Ret); // pop 1 stack size 0 type.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(9, getMaxStackMethod.Invoke(il1, null)); Assert.Equal(3, getMaxStackMethod.Invoke(il2, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body1 = typeFromDisk.GetMethod("Method1").GetMethodBody(); - MethodBody body2 = typeFromDisk.GetMethod("Method2").GetMethodBody(); - Assert.Equal(9, body1.MaxStackSize); - Assert.Equal(8, body2.MaxStackSize); // apparently doesn't write lower than 8 + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body1 = typeFromDisk.GetMethod("Method1").GetMethodBody(); + MethodBody body2 = typeFromDisk.GetMethod("Method2").GetMethodBody(); + Assert.Equal(9, body1.MaxStackSize); + Assert.Equal(8, body2.MaxStackSize); // apparently doesn't write lower than 8 + } } } @@ -261,8 +275,8 @@ public void Label_ConditionalBranching() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public, typeof(int), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public, typeof(int), [typeof(int), typeof(int)]); ILGenerator il = methodBuilder.GetILGenerator(); Label failed = il.DefineLabel(); Label endOfMethod = il.DefineLabel(); @@ -286,24 +300,27 @@ public void Label_ConditionalBranching() il.MarkLabel(endOfMethod); il.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); - Assert.Equal( - [ - (byte)OpCodes.Ldarg_1.Value, (byte)OpCodes.Ldc_I4_S.Value, 100, 0, 0, 0, + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); + Assert.Equal( + [ + (byte)OpCodes.Ldarg_1.Value, (byte)OpCodes.Ldc_I4_S.Value, 100, 0, 0, 0, (byte)OpCodes.Bgt_S.Value, 13, (byte)OpCodes.Ldarg_2.Value, (byte)OpCodes.Ldc_I4_S.Value, 100, 0, 0, 0, (byte)OpCodes.Bgt_S.Value, 5, (byte)OpCodes.Ldarg_1.Value, (byte)OpCodes.Ldarg_2.Value, (byte)OpCodes.Add.Value, (byte)OpCodes.Br_S.Value, (byte)OpCodes.Break.Value, (byte)OpCodes.Ldc_I4_M1.Value, (byte)OpCodes.Ret.Value - ], bodyBytes); + ], bodyBytes); + } } } @@ -312,8 +329,8 @@ public void Label_SwitchCase() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public, typeof(string), new[] { typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public, typeof(string), [typeof(int)]); ILGenerator il = methodBuilder.GetILGenerator(); Label defaultCase = il.DefineLabel(); Label endOfMethod = il.DefineLabel(); @@ -357,18 +374,21 @@ public void Label_SwitchCase() il.MarkLabel(endOfMethod); il.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(1, getMaxStackMethod.Invoke(il, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); - Assert.Equal((byte)OpCodes.Ldarg_1.Value, bodyBytes[0]); - Assert.Equal((byte)OpCodes.Switch.Value, bodyBytes[1]); - Assert.Equal(5, bodyBytes[2]); // case count - Assert.Equal(69, bodyBytes.Length); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); + Assert.Equal((byte)OpCodes.Ldarg_1.Value, bodyBytes[0]); + Assert.Equal((byte)OpCodes.Switch.Value, bodyBytes[1]); + Assert.Equal(5, bodyBytes[2]); // case count + Assert.Equal(69, bodyBytes.Length); + } } } @@ -377,8 +397,8 @@ public void LocalBuilderMultipleLocalsUsage() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int), typeof(string) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(string)]); ILGenerator il = methodBuilder.GetILGenerator(); LocalBuilder intLocal = il.DeclareLocal(typeof(int)); LocalBuilder stringLocal = il.DeclareLocal(typeof(string)); @@ -406,50 +426,53 @@ public void LocalBuilderMultipleLocalsUsage() type.CreateType(); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method1").GetMethodBody(); - Assert.Equal(4, body.LocalVariables.Count); - Assert.Equal(intLocal.LocalIndex, body.LocalVariables[0].LocalIndex); - Assert.Equal(intLocal.LocalType.FullName, body.LocalVariables[0].LocalType.FullName); - Assert.Equal(intLocal.IsPinned, body.LocalVariables[0].IsPinned); - Assert.Equal(stringLocal.LocalIndex, body.LocalVariables[1].LocalIndex); - Assert.Equal(stringLocal.LocalType.FullName, body.LocalVariables[1].LocalType.FullName); - Assert.Equal(stringLocal.IsPinned, body.LocalVariables[1].IsPinned); - Assert.Equal(shortLocal.LocalIndex, body.LocalVariables[2].LocalIndex); - Assert.Equal(shortLocal.LocalType.FullName, body.LocalVariables[2].LocalType.FullName); - Assert.Equal(shortLocal.IsPinned, body.LocalVariables[2].IsPinned); - Assert.Equal(int2Local.LocalIndex, body.LocalVariables[3].LocalIndex); - Assert.Equal(int2Local.LocalType.FullName, body.LocalVariables[3].LocalType.FullName); - Assert.Equal(int2Local.IsPinned, body.LocalVariables[3].IsPinned); - byte[]? bodyBytes = body.GetILAsByteArray(); - Assert.Equal((byte)OpCodes.Ldarg_1.Value, bodyBytes[0]); - Assert.Equal((byte)OpCodes.Stloc_1.Value, bodyBytes[1]); - Assert.Equal((byte)OpCodes.Ldstr.Value, bodyBytes[2]); - Assert.Equal((byte)OpCodes.Stloc_1.Value, bodyBytes[7]); - Assert.Equal((byte)OpCodes.Ldloc_1.Value, bodyBytes[8]); - Assert.Equal((byte)OpCodes.Starg_S.Value, bodyBytes[9]); - Assert.Equal((byte)OpCodes.Ldarg_0.Value, bodyBytes[11]); - Assert.Equal((byte)OpCodes.Stloc_0.Value, bodyBytes[12]); - Assert.Equal((byte)OpCodes.Ldc_I4_S.Value, bodyBytes[13]); - Assert.Equal(120, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(14, 4))); - Assert.Equal(0xFE, bodyBytes[18]); // Stloc instruction occupies 2 bytes 0xfe0e - Assert.Equal(0x0E, bodyBytes[19]); - Assert.Equal(2, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(20, 4))); // index 2 of 'il.Emit(OpCodes.Stloc, 2);' instruction - Assert.Equal((byte)OpCodes.Ldloc_2.Value, bodyBytes[24]); - Assert.Equal(0xFE, bodyBytes[25]); // Ldloc = 0xfe0c - Assert.Equal(0x0C, bodyBytes[26]); - Assert.Equal(0, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(27, 4))); // index 0 of 'il.Emit(OpCodes.Ldloc, 0);' instruction - Assert.Equal((byte)OpCodes.Add.Value, bodyBytes[31]); - Assert.Equal((byte)OpCodes.Stloc_0.Value, bodyBytes[32]); - Assert.Equal((byte)OpCodes.Ldloca_S.Value, bodyBytes[33]); - Assert.Equal(0, bodyBytes[34]); // intLocal index is 0 for 'il.Emit(OpCodes.Ldloca, intLocal);' instruction - Assert.Equal((byte)OpCodes.Ldind_I.Value, bodyBytes[35]); - Assert.Equal((byte)OpCodes.Stloc_3.Value, bodyBytes[36]); - Assert.Equal((byte)OpCodes.Ldloc_3.Value, bodyBytes[37]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[38]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method1").GetMethodBody(); + Assert.Equal(4, body.LocalVariables.Count); + Assert.Equal(intLocal.LocalIndex, body.LocalVariables[0].LocalIndex); + Assert.Equal(intLocal.LocalType.FullName, body.LocalVariables[0].LocalType.FullName); + Assert.Equal(intLocal.IsPinned, body.LocalVariables[0].IsPinned); + Assert.Equal(stringLocal.LocalIndex, body.LocalVariables[1].LocalIndex); + Assert.Equal(stringLocal.LocalType.FullName, body.LocalVariables[1].LocalType.FullName); + Assert.Equal(stringLocal.IsPinned, body.LocalVariables[1].IsPinned); + Assert.Equal(shortLocal.LocalIndex, body.LocalVariables[2].LocalIndex); + Assert.Equal(shortLocal.LocalType.FullName, body.LocalVariables[2].LocalType.FullName); + Assert.Equal(shortLocal.IsPinned, body.LocalVariables[2].IsPinned); + Assert.Equal(int2Local.LocalIndex, body.LocalVariables[3].LocalIndex); + Assert.Equal(int2Local.LocalType.FullName, body.LocalVariables[3].LocalType.FullName); + Assert.Equal(int2Local.IsPinned, body.LocalVariables[3].IsPinned); + byte[]? bodyBytes = body.GetILAsByteArray(); + Assert.Equal((byte)OpCodes.Ldarg_1.Value, bodyBytes[0]); + Assert.Equal((byte)OpCodes.Stloc_1.Value, bodyBytes[1]); + Assert.Equal((byte)OpCodes.Ldstr.Value, bodyBytes[2]); + Assert.Equal((byte)OpCodes.Stloc_1.Value, bodyBytes[7]); + Assert.Equal((byte)OpCodes.Ldloc_1.Value, bodyBytes[8]); + Assert.Equal((byte)OpCodes.Starg_S.Value, bodyBytes[9]); + Assert.Equal((byte)OpCodes.Ldarg_0.Value, bodyBytes[11]); + Assert.Equal((byte)OpCodes.Stloc_0.Value, bodyBytes[12]); + Assert.Equal((byte)OpCodes.Ldc_I4_S.Value, bodyBytes[13]); + Assert.Equal(120, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(14, 4))); + Assert.Equal(0xFE, bodyBytes[18]); // Stloc instruction occupies 2 bytes 0xfe0e + Assert.Equal(0x0E, bodyBytes[19]); + Assert.Equal(2, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(20, 4))); // index 2 of 'il.Emit(OpCodes.Stloc, 2);' instruction + Assert.Equal((byte)OpCodes.Ldloc_2.Value, bodyBytes[24]); + Assert.Equal(0xFE, bodyBytes[25]); // Ldloc = 0xfe0c + Assert.Equal(0x0C, bodyBytes[26]); + Assert.Equal(0, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(27, 4))); // index 0 of 'il.Emit(OpCodes.Ldloc, 0);' instruction + Assert.Equal((byte)OpCodes.Add.Value, bodyBytes[31]); + Assert.Equal((byte)OpCodes.Stloc_0.Value, bodyBytes[32]); + Assert.Equal((byte)OpCodes.Ldloca_S.Value, bodyBytes[33]); + Assert.Equal(0, bodyBytes[34]); // intLocal index is 0 for 'il.Emit(OpCodes.Ldloca, intLocal);' instruction + Assert.Equal((byte)OpCodes.Ldind_I.Value, bodyBytes[35]); + Assert.Equal((byte)OpCodes.Stloc_3.Value, bodyBytes[36]); + Assert.Equal((byte)OpCodes.Ldloc_3.Value, bodyBytes[37]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[38]); + } } } @@ -458,8 +481,8 @@ public void LocalBuilderMultipleTypesWithMultipleMethodsWithLocals() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public | MethodAttributes.Static, typeof(string), new[] { typeof(int), typeof(string) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder methodBuilder = type.DefineMethod("Method1", MethodAttributes.Public | MethodAttributes.Static, typeof(string), [typeof(int), typeof(string)]); ILGenerator il = methodBuilder.GetILGenerator(); LocalBuilder intLocal = il.DeclareLocal(typeof(int)); LocalBuilder stringLocal = il.DeclareLocal(typeof(string)); @@ -478,7 +501,7 @@ public void LocalBuilderMultipleTypesWithMultipleMethodsWithLocals() il.Emit(OpCodes.Stloc, intLocal); il.Emit(OpCodes.Ldloc, stringLocal); il.Emit(OpCodes.Ret); - MethodBuilder multiplyMethod = type.DefineMethod("MultiplyMethod", MethodAttributes.Public, typeof(int), new[] { typeof(int) }); + MethodBuilder multiplyMethod = type.DefineMethod("MultiplyMethod", MethodAttributes.Public, typeof(int), [typeof(int)]); ILGenerator multiplyMethodIL = multiplyMethod.GetILGenerator(); LocalBuilder iLocal = multiplyMethodIL.DeclareLocal(typeof(int)); LocalBuilder shLocal = multiplyMethodIL.DeclareLocal(typeof(short)); @@ -526,24 +549,27 @@ public void LocalBuilderMultipleTypesWithMultipleMethodsWithLocals() longMethodIL.Emit(OpCodes.Ldloc, longLocal); longMethodIL.Emit(OpCodes.Ret); anotherType.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Module moduleFromFile = assemblyFromDisk.Modules.First(); - Type typeFromDisk = moduleFromFile.GetType("MyType"); - Assert.Equal(2, typeFromDisk.GetMethod("MultiplyMethod").GetMethodBody().LocalVariables.Count); - Assert.Equal(3, typeFromDisk.GetMethod("Method1").GetMethodBody().LocalVariables.Count); - Type anotherTypeFromDisk = moduleFromFile.GetType("AnotherType"); - Assert.Equal(1, anotherTypeFromDisk.GetMethod("StringMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().LocalVariables.Count); - Assert.Equal(2, anotherTypeFromDisk.GetMethod("TypeMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().LocalVariables.Count); - Assert.Equal(2, anotherTypeFromDisk.GetMethod("LongMethod", BindingFlags.NonPublic | BindingFlags.Static).GetMethodBody().LocalVariables.Count); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module moduleFromFile = assemblyFromDisk.Modules.First(); + Type typeFromDisk = moduleFromFile.GetType("MyType"); + Assert.Equal(2, typeFromDisk.GetMethod("MultiplyMethod").GetMethodBody().LocalVariables.Count); + Assert.Equal(3, typeFromDisk.GetMethod("Method1").GetMethodBody().LocalVariables.Count); + Type anotherTypeFromDisk = moduleFromFile.GetType("AnotherType"); + Assert.Equal(1, anotherTypeFromDisk.GetMethod("StringMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().LocalVariables.Count); + Assert.Equal(2, anotherTypeFromDisk.GetMethod("TypeMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetMethodBody().LocalVariables.Count); + Assert.Equal(2, anotherTypeFromDisk.GetMethod("LongMethod", BindingFlags.NonPublic | BindingFlags.Static).GetMethodBody().LocalVariables.Count); + } } } [Fact] public void LocalBuilderExceptions() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); ILGenerator il = type.DefineMethod("Method1", MethodAttributes.Public).GetILGenerator(); ILGenerator anotherIL = type.DefineMethod("AnotherMethod", MethodAttributes.Public).GetILGenerator(); LocalBuilder stringLocal = il.DeclareLocal(typeof(string)); @@ -559,8 +585,8 @@ public void ReferenceFieldInIL() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder methodBuilder = tb.DefineMethod("Method1", MethodAttributes.Public, typeof(int), new[] { typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder methodBuilder = tb.DefineMethod("Method1", MethodAttributes.Public, typeof(int), [typeof(int)]); FieldBuilder fbNumber = tb.DefineField("_number", typeof(int), FieldAttributes.Private); Assert.Equal(0, fbNumber.MetadataToken); @@ -571,19 +597,22 @@ public void ReferenceFieldInIL() il.Emit(OpCodes.Mul); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); - Assert.Equal(9, bodyBytes.Length); - Assert.NotEqual(0, fbNumber.MetadataToken); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ldfld.Value, bodyBytes[1]); - Assert.Equal(fbNumber.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(2, 4))); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[6]); - Assert.Equal(OpCodes.Mul.Value, bodyBytes[7]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[8]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); + Assert.Equal(9, bodyBytes.Length); + Assert.NotEqual(0, fbNumber.MetadataToken); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldfld.Value, bodyBytes[1]); + Assert.Equal(fbNumber.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(2, 4))); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[6]); + Assert.Equal(OpCodes.Mul.Value, bodyBytes[7]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[8]); + } } } @@ -592,12 +621,12 @@ public void ReferenceFieldAndMethodsInIL() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder methodMain = tb.DefineMethod("Main", MethodAttributes.Public, typeof(void), new[] { typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder methodMain = tb.DefineMethod("Main", MethodAttributes.Public, typeof(void), [typeof(int)]); FieldBuilder field = tb.DefineField("_field", typeof(int), FieldAttributes.Private); - MethodInfo writeLineString = typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }); - MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", new[] { typeof(string), typeof(object), typeof(object), typeof(object) }); - MethodBuilder methodMultiply = tb.DefineMethod("Multiply", MethodAttributes.Public, typeof(int), new[] { typeof(int) }); + MethodInfo writeLineString = typeof(Console).GetMethod("WriteLine", [typeof(string)]); + MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", [typeof(string), typeof(object), typeof(object), typeof(object)]); + MethodBuilder methodMultiply = tb.DefineMethod("Multiply", MethodAttributes.Public, typeof(int), [typeof(int)]); /* class MyType { @@ -633,32 +662,35 @@ void Main(int a) ilMain.Emit(OpCodes.Call, writeLineObj); ilMain.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Call.Value, bodyBytes[5]); - // Bytes 6, 7, 8, 9 are token for writeLineString, but it is not same as the value before save - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[10]); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[15]); - Assert.Equal(OpCodes.Ldfld.Value, bodyBytes[16]); - Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(17, 4))); - Assert.Equal(OpCodes.Box.Value, bodyBytes[21]); - int intTypeToken = BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(22, 4)); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[26]); - Assert.Equal(OpCodes.Box.Value, bodyBytes[27]); - Assert.Equal(intTypeToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(28, 4))); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[32]); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[33]); - Assert.Equal(OpCodes.Call.Value, bodyBytes[34]); - Assert.Equal(methodMultiply.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(35, 4))); - Assert.Equal(OpCodes.Box.Value, bodyBytes[39]); - Assert.Equal(intTypeToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(40, 4))); - Assert.Equal(OpCodes.Call.Value, bodyBytes[44]); - // Bytes 24, 46, 47, 48 are token for writeLineObj, but it is not same as the value before save - Assert.Equal(OpCodes.Ret.Value, bodyBytes[49]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[5]); + // Bytes 6, 7, 8, 9 are token for writeLineString, but it is not same as the value before save + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[10]); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[15]); + Assert.Equal(OpCodes.Ldfld.Value, bodyBytes[16]); + Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(17, 4))); + Assert.Equal(OpCodes.Box.Value, bodyBytes[21]); + int intTypeToken = BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(22, 4)); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[26]); + Assert.Equal(OpCodes.Box.Value, bodyBytes[27]); + Assert.Equal(intTypeToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(28, 4))); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[32]); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[33]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[34]); + Assert.Equal(methodMultiply.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(35, 4))); + Assert.Equal(OpCodes.Box.Value, bodyBytes[39]); + Assert.Equal(intTypeToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(40, 4))); + Assert.Equal(OpCodes.Call.Value, bodyBytes[44]); + // Bytes 24, 46, 47, 48 are token for writeLineObj, but it is not same as the value before save + Assert.Equal(OpCodes.Ret.Value, bodyBytes[49]); + } } } @@ -667,13 +699,13 @@ public void ReferenceConstructedGenericMethod() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); ConstructorBuilder ctor = type.DefineDefaultConstructor(MethodAttributes.Public); MethodBuilder genericMethod = type.DefineMethod("GM", MethodAttributes.Public | MethodAttributes.Static); GenericTypeParameterBuilder[] methodParams = genericMethod.DefineGenericParameters("U"); genericMethod.SetSignature(null, null, null, new[] { methodParams[0] }, null, null); ILGenerator ilg = genericMethod.GetILGenerator(); - MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }); + MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", [typeof(object)]); ilg.Emit(OpCodes.Ldarg_0); ilg.EmitCall(OpCodes.Call, writeLineObj, null); ilg.Emit(OpCodes.Ret); @@ -684,17 +716,20 @@ public void ReferenceConstructedGenericMethod() ilg.EmitCall(OpCodes.Call, GMOfString, null); ilg.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, new[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodInfo genericMethodFromDisk = typeFromDisk.GetMethod("GM"); - Assert.True(genericMethodFromDisk.IsGenericMethod); - Assert.True(genericMethodFromDisk.IsGenericMethodDefinition); - byte[] ilBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldstr.Value, ilBytes[0]); - Assert.Equal(OpCodes.Call.Value, ilBytes[5]); - Assert.Equal(OpCodes.Ret.Value, ilBytes[10]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodInfo genericMethodFromDisk = typeFromDisk.GetMethod("GM"); + Assert.True(genericMethodFromDisk.IsGenericMethod); + Assert.True(genericMethodFromDisk.IsGenericMethodDefinition); + byte[] ilBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, ilBytes[0]); + Assert.Equal(OpCodes.Call.Value, ilBytes[5]); + Assert.Equal(OpCodes.Ret.Value, ilBytes[10]); + } } } @@ -703,8 +738,8 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); - GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters(new[] { "T" }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters(["T"]); ConstructorBuilder ctor = type.DefineDefaultConstructor(MethodAttributes.PrivateScope | MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); FieldBuilder myField = type.DefineField("Field", typeParams[0], FieldAttributes.Public); @@ -724,7 +759,7 @@ public void ReferenceConstructedGenericMethodFieldOfConstructedType() ilg.Emit(OpCodes.Ldloc_0); ilg.Emit(OpCodes.Ldfld, FieldOfU); ilg.Emit(OpCodes.Box, methodParams[0]); - MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }); + MethodInfo writeLineObj = typeof(Console).GetMethod("WriteLine", [typeof(object)]); ilg.EmitCall(OpCodes.Call, writeLineObj, null); ilg.Emit(OpCodes.Ret); type.CreateType(); @@ -758,34 +793,38 @@ public static void Main() MyType.GM("HelloWorld"); } } */ - saveMethod.Invoke(ab, new[] { file.Path }); - - Module module = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First(); - Type myTypeFromDisk = module.GetType("MyType"); - Assert.True(myTypeFromDisk.IsGenericType); - Assert.True(myTypeFromDisk.IsGenericTypeDefinition); - Assert.Equal("T", myTypeFromDisk.GetGenericArguments()[0].Name); - Assert.Equal("T", myTypeFromDisk.GetField("Field").FieldType.Name); - MethodInfo genericMethodFromDisk = myTypeFromDisk.GetMethod("GM"); - Assert.True(genericMethodFromDisk.IsGenericMethod); - Assert.True(genericMethodFromDisk.IsGenericMethodDefinition); - Assert.Equal(1, genericMethodFromDisk.GetMethodBody().LocalVariables.Count); - Assert.Equal("MyType[U]", genericMethodFromDisk.GetMethodBody().LocalVariables[0].LocalType.ToString()); - byte[] gmIlBytes = genericMethodFromDisk.GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Newobj.Value, gmIlBytes[0]); - Assert.Equal(OpCodes.Stloc_0.Value, gmIlBytes[5]); - Assert.Equal(OpCodes.Ldloc_0.Value, gmIlBytes[6]); - Assert.Equal(OpCodes.Ldarg_0.Value, gmIlBytes[7]); - Assert.Equal(OpCodes.Stfld.Value, gmIlBytes[8]); - Assert.Equal(OpCodes.Ldloc_0.Value, gmIlBytes[13]); - Assert.Equal(OpCodes.Ldfld.Value, gmIlBytes[14]); - Assert.Equal(OpCodes.Box.Value, gmIlBytes[19]); - Assert.Equal(OpCodes.Call.Value, gmIlBytes[24]); - Assert.Equal(OpCodes.Ret.Value, gmIlBytes[29]); - byte[] ilBytes = module.GetType("Dummy").GetMethod("Main").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldstr.Value, ilBytes[0]); - Assert.Equal(OpCodes.Call.Value, ilBytes[5]); - Assert.Equal(OpCodes.Ret.Value, ilBytes[10]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module module = assemblyFromDisk.Modules.First(); + Type myTypeFromDisk = module.GetType("MyType"); + Assert.True(myTypeFromDisk.IsGenericType); + Assert.True(myTypeFromDisk.IsGenericTypeDefinition); + Assert.Equal("T", myTypeFromDisk.GetGenericArguments()[0].Name); + Assert.Equal("T", myTypeFromDisk.GetField("Field").FieldType.Name); + MethodInfo genericMethodFromDisk = myTypeFromDisk.GetMethod("GM"); + Assert.True(genericMethodFromDisk.IsGenericMethod); + Assert.True(genericMethodFromDisk.IsGenericMethodDefinition); + Assert.Equal(1, genericMethodFromDisk.GetMethodBody().LocalVariables.Count); + Assert.Equal("MyType[U]", genericMethodFromDisk.GetMethodBody().LocalVariables[0].LocalType.ToString()); + byte[] gmIlBytes = genericMethodFromDisk.GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Newobj.Value, gmIlBytes[0]); + Assert.Equal(OpCodes.Stloc_0.Value, gmIlBytes[5]); + Assert.Equal(OpCodes.Ldloc_0.Value, gmIlBytes[6]); + Assert.Equal(OpCodes.Ldarg_0.Value, gmIlBytes[7]); + Assert.Equal(OpCodes.Stfld.Value, gmIlBytes[8]); + Assert.Equal(OpCodes.Ldloc_0.Value, gmIlBytes[13]); + Assert.Equal(OpCodes.Ldfld.Value, gmIlBytes[14]); + Assert.Equal(OpCodes.Box.Value, gmIlBytes[19]); + Assert.Equal(OpCodes.Call.Value, gmIlBytes[24]); + Assert.Equal(OpCodes.Ret.Value, gmIlBytes[29]); + byte[] ilBytes = module.GetType("Dummy").GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, ilBytes[0]); + Assert.Equal(OpCodes.Call.Value, ilBytes[5]); + Assert.Equal(OpCodes.Ret.Value, ilBytes[10]); + } } } @@ -794,7 +833,7 @@ public void EmitWriteLineMacroTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type1, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type1); MethodBuilder method = type1.DefineMethod("meth", MethodAttributes.Public, typeof(int), Type.EmptyTypes); FieldBuilder field = type1.DefineField("field", typeof(int), FieldAttributes.Public | FieldAttributes.Static); ILGenerator ilGenerator = method.GetILGenerator(); @@ -809,28 +848,31 @@ public void EmitWriteLineMacroTest() ilGenerator.Emit(OpCodes.Ldsfld, field); ilGenerator.Emit(OpCodes.Ret); type1.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("meth").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldc_I4_1.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[1]); - Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[2]); - Assert.Equal(OpCodes.Stsfld.Value, bodyBytes[3]); - Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(4, 4))); - Assert.Equal(OpCodes.Call.Value, bodyBytes[8]); - Assert.Equal(OpCodes.Ldsfld.Value, bodyBytes[13]); - Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(14, 4))); - Assert.Equal(OpCodes.Callvirt.Value, bodyBytes[18]); - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[23]); - Assert.Equal(OpCodes.Call.Value, bodyBytes[28]); - Assert.Equal(OpCodes.Call.Value, bodyBytes[33]); - Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[38]); - Assert.Equal(OpCodes.Callvirt.Value, bodyBytes[39]); - Assert.Equal(OpCodes.Ldsfld.Value, bodyBytes[44]); - Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(45, 4))); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[49]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("meth").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldc_I4_1.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[1]); + Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[2]); + Assert.Equal(OpCodes.Stsfld.Value, bodyBytes[3]); + Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(4, 4))); + Assert.Equal(OpCodes.Call.Value, bodyBytes[8]); + Assert.Equal(OpCodes.Ldsfld.Value, bodyBytes[13]); + Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(14, 4))); + Assert.Equal(OpCodes.Callvirt.Value, bodyBytes[18]); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[23]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[28]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[33]); + Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[38]); + Assert.Equal(OpCodes.Callvirt.Value, bodyBytes[39]); + Assert.Equal(OpCodes.Ldsfld.Value, bodyBytes[44]); + Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(45, 4))); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[49]); + } } } @@ -839,8 +881,8 @@ public void ReferenceStaticFieldAndMethodsInIL() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder methodMain = tb.DefineMethod("Main", MethodAttributes.Public, typeof(int), new[] { typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder methodMain = tb.DefineMethod("Main", MethodAttributes.Public, typeof(int), [typeof(int)]); TypeBuilder anotherType = ab.GetDynamicModule("MyModule").DefineType("AnotherType", TypeAttributes.Public); FieldBuilder field = anotherType.DefineField("StaticField", typeof(int), FieldAttributes.Public | FieldAttributes.Static); MethodBuilder staticMethod = anotherType.DefineMethod("StaticMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), Type.EmptyTypes); @@ -868,19 +910,22 @@ void static StaticMethod() { } il.Emit(OpCodes.Ret); tb.CreateType(); anotherType.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Call.Value, bodyBytes[0]); - Assert.Equal(staticMethod.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(1, 4))); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[5]); - Assert.Equal(OpCodes.Stsfld.Value, bodyBytes[6]); - Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(7, 4))); - Assert.Equal(OpCodes.Ldsfld.Value, bodyBytes[11]); - Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(12, 4))); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[16]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Main").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Call.Value, bodyBytes[0]); + Assert.Equal(staticMethod.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(1, 4))); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[5]); + Assert.Equal(OpCodes.Stsfld.Value, bodyBytes[6]); + Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(7, 4))); + Assert.Equal(OpCodes.Ldsfld.Value, bodyBytes[11]); + Assert.Equal(field.MetadataToken, BinaryPrimitives.ReadInt32LittleEndian(bodyBytes.AsSpan().Slice(12, 4))); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[16]); + } } } @@ -889,9 +934,9 @@ public void ReferenceConstructorInIL() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder methodBuilder = tb.DefineMethod("Method1", MethodAttributes.Public, typeof(Version), new[] { typeof(int), typeof(int) }); - ConstructorInfo ctor = typeof(Version).GetConstructor(new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder methodBuilder = tb.DefineMethod("Method1", MethodAttributes.Public, typeof(Version), [typeof(int), typeof(int)]); + ConstructorInfo ctor = typeof(Version).GetConstructor([typeof(int), typeof(int)]); ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_1); @@ -899,15 +944,18 @@ public void ReferenceConstructorInIL() il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ldarg_2.Value, bodyBytes[1]); - Assert.Equal(OpCodes.Newobj.Value, bodyBytes[2]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[7]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("Method1").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldarg_2.Value, bodyBytes[1]); + Assert.Equal(OpCodes.Newobj.Value, bodyBytes[2]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[7]); + } } } @@ -916,7 +964,7 @@ public void ReferenceAType() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); MethodBuilder method = tb.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(bool), Type.EmptyTypes); ILGenerator ilGenerator = method.GetILGenerator(); LocalBuilder lb0 = ilGenerator.DeclareLocal(typeof(ValueTuple)); @@ -925,24 +973,27 @@ public void ReferenceAType() ilGenerator.Emit(OpCodes.Ldc_I4, 1); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - byte[]? bodyBytes = typeFromDisk.GetMethod("meth1").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldloca_S.Value, bodyBytes[0]); // short form of Ldloca - Assert.Equal(0, bodyBytes[1]); - Assert.Equal(0xFE, bodyBytes[2]); // Initobj = 0xfe15 - Assert.Equal(0x15, bodyBytes[3]); - Assert.Equal(OpCodes.Ldc_I4_1.Value, bodyBytes[8]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[9]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + byte[]? bodyBytes = typeFromDisk.GetMethod("meth1").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldloca_S.Value, bodyBytes[0]); // short form of Ldloca + Assert.Equal(0, bodyBytes[1]); + Assert.Equal(0xFE, bodyBytes[2]); // Initobj = 0xfe15 + Assert.Equal(0x15, bodyBytes[3]); + Assert.Equal(OpCodes.Ldc_I4_1.Value, bodyBytes[8]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[9]); + } } } [Fact] public void MemberReferenceExceptions() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("Method1", MethodAttributes.Public); ILGenerator il = method.GetILGenerator(); MethodInfo nullMethod = null; @@ -975,8 +1026,8 @@ public void SimpleTryCatchBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), [typeof(int), typeof(int)]); Type dBZException = typeof(DivideByZeroException); ILGenerator ilGenerator = method.GetILGenerator(); LocalBuilder local = ilGenerator.DeclareLocal(typeof(float)); @@ -996,28 +1047,31 @@ public void SimpleTryCatchBlock() tb.CreateType(); Assert.Equal(3, getMaxStackMethod.Invoke(ilGenerator, null)); - saveMethod.Invoke(ab, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(1, body.ExceptionHandlingClauses.Count); - Assert.Equal(dBZException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); - byte[] bodyBytes = body.GetILAsByteArray(); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); - Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); - // Next 4 bytes 'exBlock' label location - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); // "Error: division by zero" - Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); // Calls Console.WriteLine - Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[19]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[24]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[25]); - Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[30]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[31]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(1, body.ExceptionHandlingClauses.Count); + Assert.Equal(dBZException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); + byte[] bodyBytes = body.GetILAsByteArray(); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); + Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); + // Next 4 bytes 'exBlock' label location + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); // "Error: division by zero" + Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); // Calls Console.WriteLine + Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[19]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[24]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[25]); + Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[30]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[31]); + } } } @@ -1026,8 +1080,8 @@ public void TryMultipleCatchBlocks() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), [typeof(int), typeof(int)]); Type dBZException = typeof(DivideByZeroException); Type exception = typeof(Exception); ILGenerator ilGenerator = method.GetILGenerator(); @@ -1057,40 +1111,43 @@ public void TryMultipleCatchBlocks() ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); Assert.Equal(2, maxStackField.GetValue(ilGenerator)); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(2, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(dBZException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); - byte[] bodyBytes = body.GetILAsByteArray(); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); - Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); // "Error: division by zero" - Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); // Calls Console.WriteLine - Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[19]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[24]); - Assert.Equal(OpCodes.Pop.Value, bodyBytes[25]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[26]); - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[31]); // "Error: division by zero" - Assert.Equal(OpCodes.Call.Value, bodyBytes[36]); // Calls Console.WriteLine - Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[41]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[46]); - Assert.Equal(OpCodes.Pop.Value, bodyBytes[47]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[48]); - Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[53]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[54]); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(2, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(dBZException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); + byte[] bodyBytes = body.GetILAsByteArray(); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); + Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); // "Error: division by zero" + Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); // Calls Console.WriteLine + Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[19]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[24]); + Assert.Equal(OpCodes.Pop.Value, bodyBytes[25]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[26]); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[31]); // "Error: division by zero" + Assert.Equal(OpCodes.Call.Value, bodyBytes[36]); // Calls Console.WriteLine + Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[41]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[46]); + Assert.Equal(OpCodes.Pop.Value, bodyBytes[47]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[48]); + Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[53]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[54]); + } } } @@ -1099,8 +1156,8 @@ public void TryFilterCatchBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), [typeof(int), typeof(int)]); Type dBZException = typeof(DivideByZeroException); Type exception = typeof(Exception); ILGenerator ilGenerator = method.GetILGenerator(); @@ -1139,18 +1196,21 @@ public void TryFilterCatchBlock() ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(2, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(2, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); + } } } @@ -1159,8 +1219,8 @@ public void TryCatchFilterCatchBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), [typeof(int), typeof(int)]); Type dBZException = typeof(DivideByZeroException); Type overflowException = typeof(OverflowException); Type exception = typeof(Exception); @@ -1209,20 +1269,23 @@ public void TryCatchFilterCatchBlock() ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(3, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags); - Assert.Equal(overflowException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[2].CatchType.FullName); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(3, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags); + Assert.Equal(overflowException.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[2].CatchType.FullName); + } } } @@ -1231,8 +1294,8 @@ public void TryFinallyBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), [typeof(int), typeof(int)]); ILGenerator ilGenerator = method.GetILGenerator(); LocalBuilder local = ilGenerator.DeclareLocal(typeof(float)); Label exBlock = ilGenerator.BeginExceptionBlock(); @@ -1246,27 +1309,30 @@ public void TryFinallyBlock() ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(1, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[0].Flags); - byte[] bodyBytes = body.GetILAsByteArray(); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); - Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); - Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); - Assert.Equal(OpCodes.Endfinally.Value, bodyBytes[19]); - Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[20]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[21]); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(1, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[0].Flags); + byte[] bodyBytes = body.GetILAsByteArray(); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); + Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); + Assert.Equal(OpCodes.Endfinally.Value, bodyBytes[19]); + Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[20]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[21]); + } } } @@ -1275,8 +1341,8 @@ public void TryCatchFinallyBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(void), [typeof(int), typeof(int)]); Type exception = typeof(Exception); ILGenerator ilGenerator = method.GetILGenerator(); ilGenerator.BeginExceptionBlock(); @@ -1290,18 +1356,21 @@ public void TryCatchFinallyBlock() ilGenerator.EndExceptionBlock(); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(2, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(2, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); + } } } @@ -1310,12 +1379,12 @@ public void TryFilterCatchFinallyBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); Type overflowEType = typeof(OverflowException); - ConstructorInfo myConstructorInfo = overflowEType.GetConstructor(new[] { typeof(string) }); + ConstructorInfo myConstructorInfo = overflowEType.GetConstructor([typeof(string)]); MethodInfo myExToStrMI = overflowEType.GetMethod("ToString"); - MethodInfo myWriteLineMI = typeof(Console).GetMethod("WriteLine", new[] { typeof(string), typeof(object) }); + MethodInfo myWriteLineMI = typeof(Console).GetMethod("WriteLine", [typeof(string), typeof(object)]); ILGenerator ilGenerator = method.GetILGenerator(); LocalBuilder myLocalBuilder1 = ilGenerator.DeclareLocal(typeof(int)); LocalBuilder myLocalBuilder2 = ilGenerator.DeclareLocal(overflowEType); @@ -1359,19 +1428,22 @@ public void TryFilterCatchFinallyBlock() ilGenerator.Emit(OpCodes.Ldloc_S, myLocalBuilder1); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(3, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[2].Flags); - Assert.Equal(overflowEType.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(3, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[2].Flags); + Assert.Equal(overflowEType.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); + } } } @@ -1380,8 +1452,8 @@ public void TryFaultBlock() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(float), [typeof(int), typeof(int)]); ILGenerator ilGenerator = method.GetILGenerator(); Label exBlock = ilGenerator.BeginExceptionBlock(); ilGenerator.Emit(OpCodes.Ldarg_0); @@ -1396,29 +1468,32 @@ public void TryFaultBlock() ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(1, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Fault, body.ExceptionHandlingClauses[0].Flags); - byte[] bodyBytes = body.GetILAsByteArray(); - Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); - Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); - Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); - Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); - Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); - Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); - Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[19]); - Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[24]); - Assert.Equal(OpCodes.Endfinally.Value, bodyBytes[25]); - Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[26]); - Assert.Equal(OpCodes.Ret.Value, bodyBytes[27]); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(1, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Fault, body.ExceptionHandlingClauses[0].Flags); + byte[] bodyBytes = body.GetILAsByteArray(); + Assert.Equal(OpCodes.Ldarg_0.Value, bodyBytes[0]); + Assert.Equal(OpCodes.Ldarg_1.Value, bodyBytes[1]); + Assert.Equal(OpCodes.Div.Value, bodyBytes[2]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[3]); + Assert.Equal(OpCodes.Leave.Value, bodyBytes[4]); + Assert.Equal(OpCodes.Ldstr.Value, bodyBytes[9]); + Assert.Equal(OpCodes.Call.Value, bodyBytes[14]); + Assert.Equal(OpCodes.Ldc_R4.Value, bodyBytes[19]); + Assert.Equal(OpCodes.Stloc_0.Value, bodyBytes[24]); + Assert.Equal(OpCodes.Endfinally.Value, bodyBytes[25]); + Assert.Equal(OpCodes.Ldloc_0.Value, bodyBytes[26]); + Assert.Equal(OpCodes.Ret.Value, bodyBytes[27]); + } } } @@ -1427,8 +1502,8 @@ public void NestedTryCatchBlocks() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(void), [typeof(int), typeof(int)]); Type exception = typeof(Exception); ILGenerator ilGenerator = method.GetILGenerator(); MethodInfo getMaxStackMethod = GetMaxStackMethod(); @@ -1472,20 +1547,23 @@ public void NestedTryCatchBlocks() ilGenerator.EndExceptionBlock(); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); Assert.Equal(5, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(3, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); - Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[2].CatchType.FullName); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(3, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[0].CatchType.FullName); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[1].CatchType.FullName); + Assert.Equal(exception.FullName, body.ExceptionHandlingClauses[2].CatchType.FullName); + } } } @@ -1494,8 +1572,8 @@ public void DeeperNestedTryCatchFilterFinallyBlocks() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); - MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int), typeof(int) }); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + MethodBuilder method = tb.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); Type exception = typeof(Exception); ILGenerator ilGenerator = method.GetILGenerator(); MethodInfo getMaxStackMethod = GetMaxStackMethod(); @@ -1569,20 +1647,23 @@ public void DeeperNestedTryCatchFilterFinallyBlocks() ilGenerator.Emit(OpCodes.Ldloc_0); ilGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, new object[] { file.Path }); + ab.Save(file.Path); Assert.Equal(5, getMaxStackMethod.Invoke(ilGenerator, null)); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); - Assert.Equal(6, body.ExceptionHandlingClauses.Count); - Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[0].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[1].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[3].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[4].Flags); - Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[5].Flags); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodBody body = typeFromDisk.GetMethod("Method").GetMethodBody(); + Assert.Equal(6, body.ExceptionHandlingClauses.Count); + Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[0].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Filter, body.ExceptionHandlingClauses[1].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[2].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Finally, body.ExceptionHandlingClauses[3].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[4].Flags); + Assert.Equal(ExceptionHandlingClauseOptions.Clause, body.ExceptionHandlingClauses[5].Flags); + } } } @@ -1597,7 +1678,7 @@ public void EmitCalliBlittable() int a = 1, b = 1, result = 2; using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliBlittable"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("EmitCalliBlittable")); TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); Type returnType = typeof(int); MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); @@ -1609,7 +1690,7 @@ public void EmitCalliBlittable() il.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, [typeof(int), typeof(int)]); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); @@ -1631,7 +1712,7 @@ public void EmitCalliManagedBlittable() int a = 1, b = 1, result = 2; using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliManagedBlittable"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("EmitCalliManagedBlittable")); TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); Type returnType = typeof(int); MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(int), typeof(int)]); @@ -1645,7 +1726,7 @@ public void EmitCalliManagedBlittable() il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, returnType, [typeof(int), typeof(int)], null); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); @@ -1666,10 +1747,10 @@ public void EmitCalliManagedBlittable() [Fact] public void EmitCalliNonBlittable() { - string input = "Test string!", result = "!gnirts tseT"; + string input = "Test string!", result = "!gnirts tseT"; using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("EmitCalliNonBlittable"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("EmitCalliNonBlittable")); TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); Type returnType = typeof(string); MethodBuilder methodBuilder = tb.DefineMethod("F", MethodAttributes.Public | MethodAttributes.Static, returnType, [typeof(IntPtr), typeof(string)]); @@ -1680,7 +1761,7 @@ public void EmitCalliNonBlittable() il.EmitCalli(OpCodes.Calli, CallingConvention.Cdecl, returnType, [typeof(string)]); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); @@ -1702,7 +1783,7 @@ public void EmitCall_VarArgsMethodInIL() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); MethodBuilder mb1 = tb.DefineMethod("VarArgMethod", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.VarArgs, null, [typeof(string)]); ILGenerator il1 = mb1.GetILGenerator(); LocalBuilder locAi = il1.DeclareLocal(typeof(ArgIterator)); @@ -1755,24 +1836,27 @@ public void EmitCall_VarArgsMethodInIL() il2.EmitCall(OpCodes.Call, mb1, [typeof(string), typeof(int)]); il2.Emit(OpCodes.Ret); Type type = tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - MethodInfo varArgMethodFromDisk = typeFromDisk.GetMethod("VarArgMethod"); - Assert.Equal(CallingConventions.VarArgs, varArgMethodFromDisk.CallingConvention); - ParameterInfo[] parameters = varArgMethodFromDisk.GetParameters(); - Assert.Equal(1, parameters.Length); // TODO: how to get the vararg parameter? - IList locals = varArgMethodFromDisk.GetMethodBody().LocalVariables; - Assert.Equal(2, locals.Count); - Assert.Equal(typeof(ArgIterator).FullName, locals[0].LocalType.FullName); - Assert.Equal(typeof(bool).FullName, locals[1].LocalType.FullName); - - byte[] callingMethodBody = typeFromDisk.GetMethod("CallVarArgMethod").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[0]); - Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[5]); - Assert.Equal(OpCodes.Ldc_I4.Value, callingMethodBody[10]); - Assert.Equal(OpCodes.Call.Value, callingMethodBody[15]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + MethodInfo varArgMethodFromDisk = typeFromDisk.GetMethod("VarArgMethod"); + Assert.Equal(CallingConventions.VarArgs, varArgMethodFromDisk.CallingConvention); + ParameterInfo[] parameters = varArgMethodFromDisk.GetParameters(); + Assert.Equal(1, parameters.Length); // TODO: how to get the vararg parameter? + IList locals = varArgMethodFromDisk.GetMethodBody().LocalVariables; + Assert.Equal(2, locals.Count); + Assert.Equal(typeof(ArgIterator).FullName, locals[0].LocalType.FullName); + Assert.Equal(typeof(bool).FullName, locals[1].LocalType.FullName); + + byte[] callingMethodBody = typeFromDisk.GetMethod("CallVarArgMethod").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[0]); + Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[5]); + Assert.Equal(OpCodes.Ldc_I4.Value, callingMethodBody[10]); + Assert.Equal(OpCodes.Call.Value, callingMethodBody[15]); + } } } @@ -1788,7 +1872,7 @@ public void Emit_CallBySignature() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); MethodBuilder mb1 = tb.DefineMethod("VarArgMethod", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.VarArgs, null, [typeof(string)]); ILGenerator il1 = mb1.GetILGenerator(); FieldInfo maxStack = GetMaxStackDepthAndCurrentStackDepthField(out FieldInfo currentStack); @@ -1862,19 +1946,22 @@ public void Emit_CallBySignature() Assert.Equal(0, depthAdjustment.GetValue(il2)); Assert.Equal(4, getMaxStackMethod.Invoke(il2, null)); Type type = tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - Assert.Equal(CallingConventions.VarArgs, typeFromDisk.GetMethod("VarArgMethod").CallingConvention); - - byte[] callingMethodBody = typeFromDisk.GetMethod("CallingMethod").GetMethodBody().GetILAsByteArray(); - Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[0]); - Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[5]); - Assert.Equal(OpCodes.Ldc_I4.Value, callingMethodBody[10]); - Assert.Equal(0xFE, callingMethodBody[15]); // Ldftn = 0xfe06 - Assert.Equal(0x06, callingMethodBody[16]); - Assert.Equal(OpCodes.Calli.Value, callingMethodBody[21]); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + Assert.Equal(CallingConventions.VarArgs, typeFromDisk.GetMethod("VarArgMethod").CallingConvention); + + byte[] callingMethodBody = typeFromDisk.GetMethod("CallingMethod").GetMethodBody().GetILAsByteArray(); + Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[0]); + Assert.Equal(OpCodes.Ldstr.Value, callingMethodBody[5]); + Assert.Equal(OpCodes.Ldc_I4.Value, callingMethodBody[10]); + Assert.Equal(0xFE, callingMethodBody[15]); // Ldftn = 0xfe06 + Assert.Equal(0x06, callingMethodBody[16]); + Assert.Equal(OpCodes.Calli.Value, callingMethodBody[21]); + } } } @@ -1895,7 +1982,7 @@ public void MaxStackOverflowTest() /// static void GetCode(int num) { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); var ilg = method.GetILGenerator(); @@ -1941,7 +2028,7 @@ public void MaxStackNonEmptyForward() static void GetCode(int num) { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), null); var ilg = method.GetILGenerator(); @@ -1990,7 +2077,7 @@ public void MaxStackNonEmptyBackward() static void GetCode(int num) { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); var ilg = method.GetILGenerator(); @@ -2044,8 +2131,8 @@ public void AmbiguousDepth() static void GetCode() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); - MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(bool) }); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(bool)]); var ilg = method.GetILGenerator(); // The label is targeted with stack depth zero. @@ -2075,7 +2162,7 @@ public void UnreachableDepth() static void GetCode() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("meth1", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes); var ilg = method.GetILGenerator(); @@ -2104,7 +2191,7 @@ public void SimpleForLoopTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); MethodBuilder mb2 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); ILGenerator il = mb2.GetILGenerator(); LocalBuilder sum = il.DeclareLocal(typeof(int)); @@ -2132,7 +2219,7 @@ public void SimpleForLoopTest() il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(2, getMaxStackMethod.Invoke(il, null)); @@ -2151,7 +2238,8 @@ public void RecursiveSumTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("RecursiveSumTest")); + TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); MethodBuilder mb2 = tb.DefineMethod("RecursiveMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int)]); ILGenerator il = mb2.GetILGenerator(); Label loopEnd = il.DefineLabel(); @@ -2169,7 +2257,7 @@ public void RecursiveSumTest() il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); MethodInfo getMaxStackMethod = GetMaxStackMethod(); Assert.Equal(3, getMaxStackMethod.Invoke(il, null)); @@ -2189,7 +2277,7 @@ public void CallOpenGenericMembersFromConstructedGenericType() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("M1", MethodAttributes.Public, typeof(string), null); ILGenerator ilGenerator = method.GetILGenerator(); @@ -2209,7 +2297,7 @@ public void CallOpenGenericMembersFromConstructedGenericType() ilGenerator.Emit(OpCodes.Ret); type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs index 6a3bb1e4dcbcbc..e6b5405ed81cb0 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveModuleBuilderTests.cs @@ -15,7 +15,7 @@ public void DefineGlobalMethodAndCreateGlobalFunctionsTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); MethodBuilder method = module.DefineGlobalMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public, null, null); ILGenerator ilGenerator = method.GetILGenerator(); @@ -36,7 +36,7 @@ public void DefineGlobalMethodAndCreateGlobalFunctionsTest() Assert.Equal(method2, module.GetMethod("MyMethod", [typeof(string), typeof(int)])); Assert.Equal(2, module.GetMethods().Length); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { @@ -63,7 +63,7 @@ public void DefineGlobalMethodAndCreateGlobalFunctionsTest() [Fact] public void DefineGlobalMethodAndCreateGlobalFunctions_Validations() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); Assert.Throws(() => module.DefineGlobalMethod("TestMethod", MethodAttributes.Public, null, null)); // must be static MethodBuilder method = module.DefineGlobalMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public, null, null); @@ -85,7 +85,7 @@ public static void DefinePInvokeMethodTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); DpmParams p = new DpmParams() { MethodName = "A2", LibName = "Foo2.dll", EntrypointName = "Wha2", ReturnType = typeof(int), ParameterTypes = [typeof(int)], NativeCallConv = CallingConvention.Cdecl }; @@ -95,7 +95,7 @@ public static void DefinePInvokeMethodTest() mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); modb.CreateGlobalFunctions(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); MethodInfo m = modb.GetMethod(p.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.Any, p.ParameterTypes, null); Assert.NotNull(m); @@ -117,7 +117,7 @@ public static void DefinePInvokeMethodTest() [InlineData(FieldAttributes.Assembly | FieldAttributes.SpecialName)] public void DefineUninitializedDataTest(FieldAttributes attributes) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); foreach (int size in new int[] { 1, 2, 0x003f0000 - 1 }) { @@ -133,7 +133,7 @@ public void DefineUninitializedDataTest(FieldAttributes attributes) [Fact] public void DefineUninitializedData_Validations() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); AssertExtensions.Throws("name", () => module.DefineUninitializedData(null, 1, FieldAttributes.Family)); @@ -154,7 +154,7 @@ public void DefineUninitializedData_Validations() [InlineData(FieldAttributes.Private)] public void DefineInitializedDataTest(FieldAttributes attributes) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); FieldBuilder field = module.DefineInitializedData("MyField", [01, 00, 01], attributes); @@ -167,7 +167,7 @@ public void DefineInitializedDataTest(FieldAttributes attributes) [Fact] public void DefineInitializedData_Validations() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); AssertExtensions.Throws("name", () => module.DefineInitializedData(null, [1, 0, 1], FieldAttributes.Public)); @@ -190,7 +190,7 @@ public void DefineInitializedData_EnsureAlignmentIsMinimumNeededForUseOfCreateSp { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); TypeBuilder tb = module.DefineType("MyType", TypeAttributes.Public); // Create static field data in a variety of orders that requires the runtime to actively apply alignment @@ -232,7 +232,7 @@ void CreateLoadAddressMethod(string name, FieldBuilder fieldBuilder) } checkTypeBuilder.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); @@ -265,7 +265,7 @@ void CheckMethod(string name, int minAlignmentRequired, byte[] dataToVerify) [ActiveIssue("https://github.com/dotnet/runtime/issues/96389", TestRuntimes.Mono)] public void GetABCMetadataToken_Validations() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("MyAssembly"), out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("MyAssembly")); ModuleBuilder module = ab.DefineDynamicModule("MyModule"); TypeBuilder type = module.DefineType("MyType", TypeAttributes.Public); MethodBuilder method = type.DefineMethod("TestMethod", MethodAttributes.Static | MethodAttributes.Public); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySavePropertyBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySavePropertyBuilderTests.cs index e478827125e7c7..c18eb017c14284 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySavePropertyBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySavePropertyBuilderTests.cs @@ -18,7 +18,7 @@ public void SetPropertyAccessorsAndOtherValues() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); FieldBuilder field = type.DefineField("TestField", typeof(int), FieldAttributes.Private); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.SpecialName | PropertyAttributes.HasDefault, typeof(int), null); MethodBuilder getMethod = type.DefineMethod("GetMethod", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), null); @@ -44,29 +44,32 @@ public void SetPropertyAccessorsAndOtherValues() otherILGenerator.Emit(OpCodes.Ret); property.AddOtherMethod(otherMethod); type.CreateType(); - saveMethod.Invoke(ab, new [] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - PropertyInfo propertyFromDisk = typeFromDisk.GetProperty("TestProperty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - MethodInfo getMethodFromFile = propertyFromDisk.GetGetMethod(true); - MethodInfo setMethodFromFile = propertyFromDisk.GetSetMethod(true); - Assert.Equal(getMethod.Name, getMethodFromFile.Name); - Assert.Equal(setMethod.Name, setMethodFromFile.Name); - // Not sure how other methods should have loaded/tested - // 'propertyFromDisk.GetAccessors(true)' did not return other method - Assert.NotNull(typeFromDisk.GetMethod("OtherMethod", BindingFlags.NonPublic | BindingFlags.Instance)); - Assert.True(property.CanRead); - Assert.True(property.CanWrite); - Assert.Equal(property.CanRead, propertyFromDisk.CanRead); - Assert.Equal(property.CanWrite, propertyFromDisk.CanWrite); - Assert.Equal(property.Attributes, propertyFromDisk.Attributes); - Assert.Equal(property.DeclaringType.FullName, propertyFromDisk.DeclaringType.FullName); - IList caData = propertyFromDisk.GetCustomAttributesData(); - Assert.Equal(1, caData.Count); - Assert.Equal(typeof(IntPropertyAttribute).FullName, caData[0].AttributeType.FullName); - Assert.Equal(1, caData[0].ConstructorArguments.Count); - Assert.Equal(9, caData[0].ConstructorArguments[0].Value); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + PropertyInfo propertyFromDisk = typeFromDisk.GetProperty("TestProperty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + MethodInfo getMethodFromFile = propertyFromDisk.GetGetMethod(true); + MethodInfo setMethodFromFile = propertyFromDisk.GetSetMethod(true); + Assert.Equal(getMethod.Name, getMethodFromFile.Name); + Assert.Equal(setMethod.Name, setMethodFromFile.Name); + // Not sure how other methods should have loaded/tested + // 'propertyFromDisk.GetAccessors(true)' did not return other method + Assert.NotNull(typeFromDisk.GetMethod("OtherMethod", BindingFlags.NonPublic | BindingFlags.Instance)); + Assert.True(property.CanRead); + Assert.True(property.CanWrite); + Assert.Equal(property.CanRead, propertyFromDisk.CanRead); + Assert.Equal(property.CanWrite, propertyFromDisk.CanWrite); + Assert.Equal(property.Attributes, propertyFromDisk.Attributes); + Assert.Equal(property.DeclaringType.FullName, propertyFromDisk.DeclaringType.FullName); + IList caData = propertyFromDisk.GetCustomAttributesData(); + Assert.Equal(1, caData.Count); + Assert.Equal(typeof(IntPropertyAttribute).FullName, caData[0].AttributeType.FullName); + Assert.Equal(1, caData[0].ConstructorArguments.Count); + Assert.Equal(9, caData[0].ConstructorArguments[0].Value); + } } } @@ -81,7 +84,7 @@ public void SetVariousCustomAttributes_ForProperty() PropertyInfo prop = typeof(CustomAttributeBuilder).GetProperty("Data", BindingFlags.NonPublic | BindingFlags.Instance); byte[] binaryData = (byte[])prop.GetValue(customAttrBuilder, null); - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.HasDefault, typeof(int), null); property.SetCustomAttribute(con, binaryData); property.SetCustomAttribute(new CustomAttributeBuilder(typeof(SpecialNameAttribute).GetConstructor(Type.EmptyTypes), [])); @@ -93,30 +96,33 @@ public void SetVariousCustomAttributes_ForProperty() methodILGenerator.Emit(OpCodes.Ret); property.SetGetMethod(method); type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); - PropertyInfo propertyFromDisk = typeFromDisk.GetProperty("TestProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); - Assert.True(propertyFromDisk.Attributes.HasFlag(PropertyAttributes.SpecialName)); - IList attributes = propertyFromDisk.GetCustomAttributesData(); - Assert.Equal(2, attributes.Count); - if (typeof(MaybeNullAttribute).FullName == attributes[0].AttributeType.FullName) - { - Assert.Equal(0, attributes[0].ConstructorArguments.Count); - Assert.Equal(1, attributes[1].ConstructorArguments.Count); - Assert.Equal(typeof(IntPropertyAttribute).FullName, attributes[1].AttributeType.FullName); - Assert.Equal(expectedValue, attributes[1].ConstructorArguments[0].Value); - } - else + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { - Assert.Equal(0, attributes[1].ConstructorArguments.Count); - Assert.Equal(1, attributes[0].ConstructorArguments.Count); - Assert.Equal(typeof(IntPropertyAttribute).FullName, attributes[0].AttributeType.FullName); - Assert.Equal(expectedValue, attributes[0].ConstructorArguments[0].Value); + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + PropertyInfo propertyFromDisk = typeFromDisk.GetProperty("TestProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); + Assert.True(propertyFromDisk.Attributes.HasFlag(PropertyAttributes.SpecialName)); + IList attributes = propertyFromDisk.GetCustomAttributesData(); + Assert.Equal(2, attributes.Count); + if (typeof(MaybeNullAttribute).FullName == attributes[0].AttributeType.FullName) + { + Assert.Equal(0, attributes[0].ConstructorArguments.Count); + Assert.Equal(1, attributes[1].ConstructorArguments.Count); + Assert.Equal(typeof(IntPropertyAttribute).FullName, attributes[1].AttributeType.FullName); + Assert.Equal(expectedValue, attributes[1].ConstructorArguments[0].Value); + } + else + { + Assert.Equal(0, attributes[1].ConstructorArguments.Count); + Assert.Equal(1, attributes[0].ConstructorArguments.Count); + Assert.Equal(typeof(IntPropertyAttribute).FullName, attributes[0].AttributeType.FullName); + Assert.Equal(expectedValue, attributes[0].ConstructorArguments[0].Value); + } + Assert.Empty(attributes[0].NamedArguments); + Assert.Empty(attributes[1].NamedArguments); } - Assert.Empty(attributes[0].NamedArguments); - Assert.Empty(attributes[1].NamedArguments); } } @@ -149,7 +155,7 @@ public static IEnumerable SetConstant_TestData() [MemberData(nameof(SetConstant_TestData))] public void SetConstantVariousValues(Type returnType, object defaultValue) { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.HasDefault, returnType, null); property.SetConstant(defaultValue); @@ -160,7 +166,7 @@ public void SetConstantVariousValues(Type returnType, object defaultValue) [Fact] public void SetCustomAttribute_ConstructorInfo_ByteArray_NullConstructorInfo_ThrowsArgumentNullException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.HasDefault, typeof(int), null); AssertExtensions.Throws("con", () => property.SetCustomAttribute(null, new byte[6])); @@ -169,7 +175,7 @@ public void SetCustomAttribute_ConstructorInfo_ByteArray_NullConstructorInfo_Thr [Fact] public void Set_NullValue_ThrowsArgumentNullException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.None, typeof(int), null); AssertExtensions.Throws("mdBuilder", () => property.SetGetMethod(null)); @@ -181,7 +187,7 @@ public void Set_NullValue_ThrowsArgumentNullException() [Fact] public void Set_WhenTypeAlreadyCreated_ThrowsInvalidOperationException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); FieldBuilder field = type.DefineField("TestField", typeof(int), FieldAttributes.Private); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.HasDefault, typeof(int), null); @@ -202,7 +208,7 @@ public void Set_WhenTypeAlreadyCreated_ThrowsInvalidOperationException() [Fact] public void SetConstant_ValidationThrows() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); FieldBuilder field = type.DefineField("TestField", typeof(int), FieldAttributes.Private); PropertyBuilder property = type.DefineProperty("TestProperty", PropertyAttributes.HasDefault, typeof(int), null); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs index 65762a54b4751b..dc083e521815bc 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTools.cs @@ -31,13 +31,12 @@ internal static class AssemblySaveTools internal static void WriteAssemblyToDisk(AssemblyName assemblyName, Type[] types, string fileLocation) { - AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilderAndSaveMethod( - assemblyName, null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilder(assemblyName); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name); PopulateMembersForModule(mb, types); - saveMethod.Invoke(assemblyBuilder, new object[] { fileLocation }); + assemblyBuilder.Save(fileLocation); } private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types) @@ -68,44 +67,23 @@ private static void PopulateMembersForModule(ModuleBuilder mb, Type[] types) internal static void WriteAssemblyToStream(AssemblyName assemblyName, Type[] types, Stream stream) { - AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilderAndSaveMethod( - assemblyName, null, typeof(Stream), out MethodInfo saveMethod); + AssemblyBuilder assemblyBuilder = PopulateAssemblyBuilder(assemblyName); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule(assemblyName.Name); PopulateMembersForModule(mb, types); - saveMethod.Invoke(assemblyBuilder, new object[] { stream }); + assemblyBuilder.Save(stream); } - internal static AssemblyBuilder PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder typeBuilder, out MethodInfo saveMethod) + internal static AssemblyBuilder PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeBuilder) { - AssemblyBuilder ab = PopulateAssemblyBuilderAndSaveMethod(s_assemblyName, null, typeof(string), out saveMethod); + AssemblyBuilder ab = PopulateAssemblyBuilder(s_assemblyName, null); typeBuilder = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); return ab; } - internal static AssemblyBuilder PopulateAssemblyBuilderAndSaveMethod(AssemblyName assemblyName, out MethodInfo saveMethod) => - PopulateAssemblyBuilderAndSaveMethod(assemblyName, null, typeof(string), out saveMethod); - - internal static AssemblyBuilder PopulateAssemblyBuilderAndSaveMethod(AssemblyName assemblyName, - List? assemblyAttributes, Type parameterType, out MethodInfo saveMethod) - { - Type assemblyType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!; - - saveMethod = assemblyType.GetMethod("Save", BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { parameterType }); - - MethodInfo defineDynamicAssemblyMethod = assemblyType.GetMethod("DefinePersistedAssembly", BindingFlags.NonPublic | BindingFlags.Static, - new Type[] { typeof(AssemblyName), typeof(Assembly), typeof(List) }); - - return (AssemblyBuilder)defineDynamicAssemblyMethod.Invoke(null, - new object[] { assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes }); - } - - internal static Assembly LoadAssemblyFromPath(string filePath) => - new MetadataLoadContext(new CoreMetadataAssemblyResolver()).LoadFromAssemblyPath(filePath); - - internal static Assembly LoadAssemblyFromStream(Stream stream) => - new MetadataLoadContext(new CoreMetadataAssemblyResolver()).LoadFromStream(stream); + internal static AssemblyBuilder PopulateAssemblyBuilder(AssemblyName assemblyName, List? assemblyAttributes = null) => + AssemblyBuilder.DefinePersistedAssembly(assemblyName, CoreMetadataAssemblyResolver.s_coreAssembly, assemblyAttributes); internal static void AssertAssemblyNameAndModule(AssemblyName sourceAName, AssemblyName aNameFromDisk, Module moduleFromDisk) { diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index 88755b56714897..e38e3e1d6582cd 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -17,7 +17,7 @@ public void DefineMethodOverride_InterfaceMethod() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("MImpl", MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), null); ILGenerator ilGenerator = method.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldc_I4, 2); @@ -26,7 +26,7 @@ public void DefineMethodOverride_InterfaceMethod() MethodInfo declaration = typeof(DefineMethodOverrideInterface).GetMethod("M"); type.DefineMethodOverride(method, declaration); type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); InterfaceMapping im = type.GetInterfaceMap(typeof(DefineMethodOverrideInterface)); Assert.Equal(type, im.TargetType); @@ -35,9 +35,12 @@ public void DefineMethodOverride_InterfaceMethod() Assert.Equal(declaration, im.InterfaceMethods[0]); Assert.Equal(method, im.TargetMethods[0]); - Type typeFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path).GetType("MyType"); - MethodInfo methodFromDisk = typeFromDisk.GetMethod("MImpl"); - Assert.True(methodFromDisk.IsVirtual); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("MImpl"); + Assert.True(methodFromDisk.IsVirtual); + } } } @@ -46,7 +49,7 @@ public void DefineMethodOverride_BaseTypeImplementation() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.SetParent(typeof(DefineMethodOverrideClass)); MethodBuilder method = type.DefineMethod("M2", MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), null); ILGenerator ilGenerator = method.GetILGenerator(); @@ -55,10 +58,13 @@ public void DefineMethodOverride_BaseTypeImplementation() MethodInfo declaration = typeof(DefineMethodOverrideClass).GetMethod("M"); type.DefineMethodOverride(method, declaration); Type createdType = type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); - Type typeFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path).GetType("MyType"); - Assert.True(typeFromDisk.GetMethod("M2").IsVirtual); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + Assert.True(typeFromDisk.GetMethod("M2").IsVirtual); + } } } @@ -67,7 +73,7 @@ public void DefineMethodOverride_GenericInterface_Succeeds() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.AddInterfaceImplementation(typeof(GenericInterface)); MethodBuilder method = type.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Virtual, typeof(string), Type.EmptyTypes); ILGenerator ilGenerator = method.GetILGenerator(); @@ -75,25 +81,28 @@ public void DefineMethodOverride_GenericInterface_Succeeds() ilGenerator.Emit(OpCodes.Ret); type.DefineMethodOverride(method, typeof(GenericInterface).GetMethod("Method")); Type createdType = type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); - Type typeFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path).GetType("MyType"); - MethodInfo methodFromDisk = typeFromDisk.GetMethod("Method"); - Assert.True(methodFromDisk.IsVirtual); - - InterfaceMapping im = type.GetInterfaceMap(typeof(GenericInterface)); - Assert.Equal(type, im.TargetType); - Assert.Equal(typeof(GenericInterface), im.InterfaceType); - Assert.Equal(1, im.InterfaceMethods.Length); - Assert.Equal(typeof(GenericInterface).GetMethod("Method"), im.InterfaceMethods[0]); - Assert.Equal(method, im.TargetMethods[0]); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + MethodInfo methodFromDisk = typeFromDisk.GetMethod("Method"); + Assert.True(methodFromDisk.IsVirtual); + + InterfaceMapping im = type.GetInterfaceMap(typeof(GenericInterface)); + Assert.Equal(type, im.TargetType); + Assert.Equal(typeof(GenericInterface), im.InterfaceType); + Assert.Equal(1, im.InterfaceMethods.Length); + Assert.Equal(typeof(GenericInterface).GetMethod("Method"), im.InterfaceMethods[0]); + Assert.Equal(method, im.TargetMethods[0]); + } } } [Fact] public void DefineMethodOverride_NullMethodInfoBody_ThrowsArgumentNullException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodInfo method = typeof(DefineMethodOverrideClass).GetMethod("M"); MethodInfo imethod = typeof(DefineMethodOverrideInterface).GetMethod("M"); @@ -104,7 +113,7 @@ public void DefineMethodOverride_NullMethodInfoBody_ThrowsArgumentNullException( [Fact] public void DefineMethodOverride_MethodNotInClass_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodInfo body = typeof(DefineMethodOverrideInterface).GetMethod("M"); MethodInfo declaration = typeof(DefineMethodOverrideClass).GetMethod("M"); @@ -114,7 +123,7 @@ public void DefineMethodOverride_MethodNotInClass_ThrowsArgumentException() [Fact] public void DefineMethodOverride_TypeCreated_ThrowsInvalidOperationException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), null); method.GetILGenerator().Emit(OpCodes.Ret); type.AddInterfaceImplementation(typeof(DefineMethodOverrideInterface)); @@ -128,7 +137,7 @@ public void DefineMethodOverride_TypeCreated_ThrowsInvalidOperationException() [Fact] public void DefineMethodOverride_MethodNotVirtual_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("M", MethodAttributes.Public, typeof(int), null); ILGenerator ilGenerator = method.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldc_I4, 2); @@ -143,7 +152,7 @@ public void DefineMethodOverride_MethodNotVirtual_ThrowsArgumentException() [Fact] public void DefineMethodOverride_TypeDoesNotImplementOrInheritMethod_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), null); method.GetILGenerator().Emit(OpCodes.Ret); MethodInfo interfaceMethod = typeof(DefineMethodOverrideInterface).GetMethod("M"); @@ -160,7 +169,7 @@ public void DefineMethodOverride_TypeDoesNotImplementOrInheritMethod_ThrowsArgum [Fact] public void DefineMethodOverride_CalledAgainWithSameDeclaration_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method1 = type.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), null); ILGenerator ilGenerator1 = method1.GetILGenerator(); ilGenerator1.Emit(OpCodes.Ldc_I4, 1); @@ -187,7 +196,7 @@ public void DefineMethodOverride_CalledAgainWithSameDeclaration_ThrowsArgumentEx [InlineData(typeof(string), new Type[] { typeof(string), typeof(int) })] public void DefineMethodOverride_BodyAndDeclarationHaveDifferentSignatures_ThrowsArgumentException(Type returnType, Type[] parameterTypes) { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder method = type.DefineMethod("M", MethodAttributes.Public | MethodAttributes.Virtual, returnType, parameterTypes); method.GetILGenerator().Emit(OpCodes.Ret); type.AddInterfaceImplementation(typeof(InterfaceWithMethod)); @@ -210,7 +219,7 @@ public interface InterfaceWithMethod [Fact] public void DefineMethodOverride_StaticVirtualInterfaceMethodWorks() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); ModuleBuilder module = ab.GetDynamicModule("MyModule"); TypeBuilder interfaceType = module.DefineType("InterfaceType", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract, parent: null); @@ -240,7 +249,7 @@ public abstract class Impl : InterfaceWithMethod [Fact] public void GetInterfaceMap_WithImplicitOverride_DefineMethodOverride() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); ModuleBuilder module = ab.GetDynamicModule("MyModule"); TypeBuilder interfaceType = module.DefineType("InterfaceType", TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract, parent: null); @@ -294,7 +303,7 @@ public void GetInterfaceMap_WithImplicitOverride_DefineMethodOverride() [Fact] public void GetInterfaceMap_Validations() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.AddInterfaceImplementation(typeof(DefineMethodOverrideInterface)); Assert.Throws(() => type.GetInterfaceMap(typeof(Impl))); // concreteTypeWithAbstractMethod not created @@ -331,7 +340,7 @@ public interface IStaticAbstract [Fact] public void CreateType_ValidateAllAbstractMethodsAreImplemented() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder typeNotImplementedIfaceMethod, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder typeNotImplementedIfaceMethod); typeNotImplementedIfaceMethod.AddInterfaceImplementation(typeof(DefineMethodOverrideInterface)); ModuleBuilder module = ab.GetDynamicModule("MyModule"); TypeBuilder partiallyImplementedType = module.DefineType("Type2", TypeAttributes.Public); @@ -359,7 +368,7 @@ public void CreateType_ValidateAllAbstractMethodsAreImplemented() [Fact] public void CreateType_ValidateMethods() { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder concreteTypeWithAbstractMethod, out MethodInfo _); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder concreteTypeWithAbstractMethod); concreteTypeWithAbstractMethod.DefineMethod("AbstractMethod", MethodAttributes.Public | MethodAttributes.Abstract); Assert.Throws(() => concreteTypeWithAbstractMethod.CreateType()); // Type must be declared abstract if any of its methods are abstract. @@ -386,7 +395,7 @@ public void CreateType_ValidateMethods() [Fact] public void GetMethodsGetMethodImpl_Tests() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder voidPublicMethod = type.DefineMethod("VoidMethod", MethodAttributes.Public, typeof(void), [typeof(int)]); MethodBuilder voidAssemblyStaticMethod = type.DefineMethod("VoidMethod", MethodAttributes.Assembly | MethodAttributes.Static, typeof(void), Type.EmptyTypes); MethodBuilder voidFamilyOrAssemblyMethod = type.DefineMethod("VoidMethod", MethodAttributes.FamORAssem, typeof(void), Type.EmptyTypes); @@ -425,14 +434,14 @@ public void ReturnTypeAndParameterRequiredOptionalCustomModifiers() Type[] cmodsReq2 = [typeof(uint)]; Type[] cmodsOpt1 = [typeof(int)]; Type[] cmodsOpt2 = [typeof(long), typeof(byte), typeof(bool)]; - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); MethodBuilder methodAll = type.DefineMethod("AllModifiers", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(string), [typeof(int), typeof(short)], [typeof(Version)], [typeof(int), typeof(long)], [cmodsReq1, cmodsReq2], [cmodsOpt1, cmodsOpt2]); ILGenerator ilGenerator = methodAll.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldstr, "Hello World"); ilGenerator.Emit(OpCodes.Ret); Type createdType = type.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { @@ -466,7 +475,7 @@ public static void DefinePInvokeMethodExecution_Windows() using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(new AssemblyName("DefinePInvokeMethodExecution_Windows"), out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("DefinePInvokeMethodExecution_Windows")); TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class); MethodBuilder mb = tb.DefinePInvokeMethod( "GetEnvironmentVariableW", @@ -480,7 +489,7 @@ public static void DefinePInvokeMethodExecution_Windows() mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); Type t = tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); @@ -535,12 +544,12 @@ public static void TestDefinePInvokeMethod(DpmParams p) { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); MethodBuilder mb = tb.DefinePInvokeMethod(p.MethodName, p.LibName, p.EntrypointName, p.Attributes, p.ManagedCallConv, p.ReturnType, p.ReturnTypeReqMods, p.ReturnTypeOptMods, p.ParameterTypes, p.ParameterTypeReqMods, p.ParameterTypeOptMods, p.NativeCallConv, p.Charset); mb.SetImplementationFlags(mb.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig); Type t = tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) { @@ -667,7 +676,7 @@ public void DefineTypeInitializer() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); FieldBuilder greetingField = tb.DefineField("Greeting", typeof(string), FieldAttributes.Private | FieldAttributes.Static); ConstructorBuilder constructor = tb.DefineTypeInitializer(); ILGenerator constructorIlGenerator = constructor.GetILGenerator(); @@ -676,7 +685,7 @@ public void DefineTypeInitializer() constructorIlGenerator.Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); @@ -691,7 +700,7 @@ public static void DefineUninitializedDataTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder tb, out MethodInfo saveMethod); + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); FieldBuilder myFieldBuilder = tb.DefineUninitializedData("MyGreeting", 4, FieldAttributes.Public); var loadAddressMethod = tb.DefineMethod("LoadAddress", MethodAttributes.Public | MethodAttributes.Static, typeof(IntPtr), null); var methodIL = loadAddressMethod.GetILGenerator(); @@ -699,7 +708,7 @@ public static void DefineUninitializedDataTest() methodIL.Emit(OpCodes.Ret); Type t = tb.CreateType(); - saveMethod.Invoke(ab, [file.Path]); + ab.Save(file.Path); TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); Assembly assemblyFromDisk = tlc.LoadFromAssemblyPath(file.Path); diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 8e6d7227c02a7b..bd510e83c65987 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -25,20 +25,16 @@ public void EmptyAssemblyAndModuleTest() { using (TempFile file = TempFile.Create()) { - Assembly assemblyFromDisk = WriteAndLoadAssembly(Type.EmptyTypes, file.Path); - - Assert.Empty(assemblyFromDisk.GetTypes()); - AssemblySaveTools.AssertAssemblyNameAndModule(s_assemblyName, assemblyFromDisk.GetName(), assemblyFromDisk.Modules.FirstOrDefault()); + AssemblySaveTools.WriteAssemblyToDisk(s_assemblyName, Type.EmptyTypes, file.Path); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Assert.Empty(assemblyFromDisk.GetTypes()); + AssemblySaveTools.AssertAssemblyNameAndModule(s_assemblyName, assemblyFromDisk.GetName(), assemblyFromDisk.Modules.FirstOrDefault()); + } } } - private static Assembly WriteAndLoadAssembly(Type[] types, string filePath) - { - AssemblySaveTools.WriteAssemblyToDisk(s_assemblyName, types, filePath); - - return AssemblySaveTools.LoadAssemblyFromPath(filePath); - } - public static IEnumerable VariousInterfacesStructsTestData() { yield return new object[] { new Type[] { typeof(INoMethod) } }; @@ -58,9 +54,13 @@ public void WriteAssemblyWithVariousTypesToAFileAndReadBackTest(Type[] types) { using (TempFile file = TempFile.Create()) { - Assembly assemblyFromDisk = WriteAndLoadAssembly(types, file.Path); + AssemblySaveTools.WriteAssemblyToDisk(s_assemblyName, types, file.Path); - AssertTypesAndTypeMembers(types, assemblyFromDisk.Modules.First().GetTypes()); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + AssertTypesAndTypeMembers(types, assemblyFromDisk.Modules.First().GetTypes()); + } } } @@ -86,9 +86,12 @@ public void WriteAssemblyWithVariousTypesToStreamAndReadBackTest(Type[] types) using (var stream = new MemoryStream()) { AssemblySaveTools.WriteAssemblyToStream(s_assemblyName, types, stream); - Assembly assemblyFromStream = AssemblySaveTools.LoadAssemblyFromStream(stream); - AssertTypesAndTypeMembers(types, assemblyFromStream.Modules.First().GetTypes()); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromStream = mlc.LoadFromStream(stream); + AssertTypesAndTypeMembers(types, assemblyFromStream.Modules.First().GetTypes()); + } } } @@ -97,30 +100,33 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); tb.DefineMethod("TestMethod", MethodAttributes.Public).GetILGenerator().Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + assemblyBuilder.Save(file.Path); - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Module moduleFromDisk = assemblyFromDisk.Modules.First(); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Module moduleFromDisk = assemblyFromDisk.Modules.First(); - Assert.Equal("MyModule", moduleFromDisk.ScopeName); - Assert.Equal(1, moduleFromDisk.GetTypes().Length); + Assert.Equal("MyModule", moduleFromDisk.ScopeName); + Assert.Equal(1, moduleFromDisk.GetTypes().Length); - Type testType = moduleFromDisk.GetTypes()[0]; - Assert.Equal("TestInterface", testType.Name); + Type testType = moduleFromDisk.GetTypes()[0]; + Assert.Equal("TestInterface", testType.Name); - MethodInfo method = testType.GetMethods()[0]; - Assert.Equal("TestMethod", method.Name); - Assert.Empty(method.GetParameters()); - Assert.Equal("System.Void", method.ReturnType.FullName); + MethodInfo method = testType.GetMethods()[0]; + Assert.Equal("TestMethod", method.Name); + Assert.Empty(method.GetParameters()); + Assert.Equal("System.Void", method.ReturnType.FullName); + } } } - private static TypeBuilder CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod) + private static TypeBuilder CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder) { - assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(s_assemblyName, null, typeof(string), out saveMethod); + assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(s_assemblyName); return assemblyBuilder.DefineDynamicModule("MyModule") .DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract); } @@ -130,29 +136,31 @@ public void AddInterfaceImplementationTest() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( - s_assemblyName, null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(s_assemblyName); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); - TypeBuilder tb = mb.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract, null, new Type[] { typeof(IOneMethod)}); + TypeBuilder tb = mb.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract, null, [typeof(IOneMethod)]); tb.AddInterfaceImplementation(typeof(INoMethod)); TypeBuilder nestedType = tb.DefineNestedType("NestedType", TypeAttributes.Interface | TypeAttributes.Abstract); tb.CreateType(); nestedType.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path); - Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; - Type[] interfaces = testType.GetInterfaces(); - - Assert.Equal("TestInterface", testType.Name); - Assert.Equal(2, interfaces.Length); + assemblyBuilder.Save(file.Path); - Type iOneMethod = testType.GetInterface("IOneMethod"); - Type iNoMethod = testType.GetInterface("INoMethod"); - Type[] nt = testType.GetNestedTypes(); - Assert.Equal(1, iOneMethod.GetMethods().Length); - Assert.Empty(iNoMethod.GetMethods()); - Assert.NotNull(testType.GetNestedType("NestedType", BindingFlags.NonPublic)); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; + Type[] interfaces = testType.GetInterfaces(); + + Assert.Equal("TestInterface", testType.Name); + Assert.Equal(2, interfaces.Length); + + Type iOneMethod = testType.GetInterface("IOneMethod"); + Type iNoMethod = testType.GetInterface("INoMethod"); + Type[] nt = testType.GetNestedTypes(); + Assert.Equal(1, iOneMethod.GetMethods().Length); + Assert.Empty(iNoMethod.GetMethods()); + Assert.NotNull(testType.GetNestedType("NestedType", BindingFlags.NonPublic)); + } } } @@ -168,7 +176,7 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames) { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); MethodBuilder method = tb.DefineMethod("TestMethod", MethodAttributes.Public); method.GetILGenerator().Emit(OpCodes.Ldarg_0); GenericTypeParameterBuilder[] typeParams = tb.DefineGenericParameters(typeParamNames); @@ -177,27 +185,31 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames) SetVariousGenericParameterValues(typeParams); } tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); - Type[] genericTypeParams = testType.GetGenericArguments(); - - Assert.True(testType.IsGenericType); - Assert.True(testType.IsGenericTypeDefinition); - Assert.True(testType.ContainsGenericParameters); - Assert.False(testMethod.IsGenericMethod); - Assert.False(testMethod.IsGenericMethodDefinition); - Assert.True(testMethod.ContainsGenericParameters); - AssertGenericParameters(typeParams, genericTypeParams); + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type testType = assemblyFromDisk.Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); + Type[] genericTypeParams = testType.GetGenericArguments(); + + Assert.True(testType.IsGenericType); + Assert.True(testType.IsGenericTypeDefinition); + Assert.True(testType.ContainsGenericParameters); + Assert.False(testMethod.IsGenericMethod); + Assert.False(testMethod.IsGenericMethodDefinition); + Assert.True(testMethod.ContainsGenericParameters); + AssertGenericParameters(typeParams, genericTypeParams); + } } } private static void SetVariousGenericParameterValues(GenericTypeParameterBuilder[] typeParams) { - typeParams[0].SetInterfaceConstraints(new Type[] { typeof(IAccess), typeof(INoMethod) }); + typeParams[0].SetInterfaceConstraints([typeof(IAccess), typeof(INoMethod)]); typeParams[1].SetCustomAttribute(new CustomAttributeBuilder(typeof(DynamicallyAccessedMembersAttribute).GetConstructor( - new Type[] { typeof(DynamicallyAccessedMemberTypes) }), new object[] { DynamicallyAccessedMemberTypes.PublicProperties })); + [typeof(DynamicallyAccessedMemberTypes)]), [DynamicallyAccessedMemberTypes.PublicProperties])); typeParams[2].SetBaseTypeConstraint(typeof(EmptyTestClass)); typeParams[2].SetGenericParameterAttributes(GenericParameterAttributes.VarianceMask); } @@ -233,7 +245,7 @@ public void SaveGenericTypeParametersForAMethod(string[] typeParamNames) { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); MethodBuilder method = tb.DefineMethod("TestMethod", MethodAttributes.Public); GenericTypeParameterBuilder[] typeParams = method.DefineGenericParameters(typeParamNames); method.GetILGenerator().Emit(OpCodes.Ldarg_0); @@ -242,19 +254,22 @@ public void SaveGenericTypeParametersForAMethod(string[] typeParamNames) SetVariousGenericParameterValues(typeParams); } tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); - Type[] genericTypeParams = testMethod.GetGenericArguments(); - - Assert.False(testType.IsGenericType); - Assert.False(testType.IsGenericTypeDefinition); - Assert.False(testType.ContainsGenericParameters); - Assert.True(testMethod.IsGenericMethod); - Assert.True(testMethod.IsGenericMethodDefinition); - Assert.True(testMethod.ContainsGenericParameters); - AssertGenericParameters(typeParams, genericTypeParams); + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); + Type[] genericTypeParams = testMethod.GetGenericArguments(); + + Assert.False(testType.IsGenericType); + Assert.False(testType.IsGenericTypeDefinition); + Assert.False(testType.ContainsGenericParameters); + Assert.True(testMethod.IsGenericMethod); + Assert.True(testMethod.IsGenericMethodDefinition); + Assert.True(testMethod.ContainsGenericParameters); + AssertGenericParameters(typeParams, genericTypeParams); + } } } @@ -267,25 +282,28 @@ public void SaveArrayTypeSignature(int rank, string name) { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); Type arrayType = rank == 0 ? tb.MakeArrayType() : tb.MakeArrayType(rank); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(arrayType); - mb.SetParameters(new Type[] { typeof(INoMethod), arrayType, typeof(int[,,,]) }); + mb.SetParameters([typeof(INoMethod), arrayType, typeof(int[,,,])]); mb.GetILGenerator().Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); - Type intArray = testMethod.GetParameters()[2].ParameterType; - - Assert.False(testMethod.GetParameters()[0].ParameterType.IsSZArray); - Assert.True(intArray.IsArray); - Assert.Equal(4, intArray.GetArrayRank()); - Assert.Equal("Int32[,,,]", intArray.Name); - AssertArrayTypeSignature(rank, name, testMethod.ReturnType); - AssertArrayTypeSignature(rank, name, testMethod.GetParameters()[1].ParameterType); + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); + Type intArray = testMethod.GetParameters()[2].ParameterType; + + Assert.False(testMethod.GetParameters()[0].ParameterType.IsSZArray); + Assert.True(intArray.IsArray); + Assert.Equal(4, intArray.GetArrayRank()); + Assert.Equal("Int32[,,,]", intArray.Name); + AssertArrayTypeSignature(rank, name, testMethod.ReturnType); + AssertArrayTypeSignature(rank, name, testMethod.GetParameters()[1].ParameterType); + } } } @@ -302,21 +320,24 @@ public void SaveByRefTypeSignature() { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); Type byrefType = tb.MakeByRefType(); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(byrefType); - mb.SetParameters(new Type[] { typeof(INoMethod), byrefType }); + mb.SetParameters([typeof(INoMethod), byrefType]); mb.GetILGenerator().Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + assemblyBuilder.Save(file.Path); - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); - Assert.False(testMethod.GetParameters()[0].ParameterType.IsByRef); - AssertByRefType(testMethod.GetParameters()[1].ParameterType); - AssertByRefType(testMethod.ReturnType); + Assert.False(testMethod.GetParameters()[0].ParameterType.IsByRef); + AssertByRefType(testMethod.GetParameters()[1].ParameterType); + AssertByRefType(testMethod.ReturnType); + } } } @@ -331,21 +352,24 @@ public void SavePointerTypeSignature() { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); Type pointerType = tb.MakePointerType(); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(pointerType); - mb.SetParameters(new Type[] { typeof(INoMethod), pointerType }); + mb.SetParameters([typeof(INoMethod), pointerType]); mb.GetILGenerator().Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + assemblyBuilder.Save(file.Path); - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); - Assert.False(testMethod.GetParameters()[0].ParameterType.IsPointer); - AssertPointerType(testMethod.GetParameters()[1].ParameterType); - AssertPointerType(testMethod.ReturnType); + Assert.False(testMethod.GetParameters()[0].ParameterType.IsPointer); + AssertPointerType(testMethod.GetParameters()[1].ParameterType); + AssertPointerType(testMethod.ReturnType); + } } } @@ -369,30 +393,33 @@ public void SaveGenericTypeSignature(string[] genericParams, Type[] typeArgument { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); GenericTypeParameterBuilder[] typeGenParam = tb.DefineGenericParameters(genericParams); Type genericType = tb.MakeGenericType(typeArguments); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); mb.SetReturnType(genericType); - mb.SetParameters(new Type[] { typeof(INoMethod), genericType }); + mb.SetParameters([typeof(INoMethod), genericType]); mb.GetILGenerator().Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); + assemblyBuilder.Save(file.Path); - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); - Type paramType = testMethod.GetParameters()[1].ParameterType; + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); + Type paramType = testMethod.GetParameters()[1].ParameterType; - Assert.False(testMethod.GetParameters()[0].ParameterType.IsGenericType); - AssertGenericType(stringRepresentation, paramType); - AssertGenericType(stringRepresentation, testMethod.ReturnType); + Assert.False(testMethod.GetParameters()[0].ParameterType.IsGenericType); + AssertGenericType(stringRepresentation, paramType); + AssertGenericType(stringRepresentation, testMethod.ReturnType); + } } } [Fact] public void TypeBuilder_GetMethod_ReturnsMethod() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); type.DefineGenericParameters("T"); MethodBuilder genericMethod = type.DefineMethod("GM", MethodAttributes.Public | MethodAttributes.Static); @@ -412,7 +439,7 @@ public void TypeBuilder_GetMethod_ReturnsMethod() [Fact] public void TypeBuilder_GetField_DeclaringTypeOfFieldGeneric() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters("T"); FieldBuilder field = type.DefineField("Field", typeParams[0].AsType(), FieldAttributes.Public); @@ -426,7 +453,7 @@ public void TypeBuilder_GetField_DeclaringTypeOfFieldGeneric() [Fact] public void GetField_TypeNotGeneric_ThrowsArgumentException() { - AssemblySaveTools.PopulateAssemblyBuilderTypeBuilderAndSaveMethod(out TypeBuilder type, out MethodInfo _); + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); FieldBuilder field = type.DefineField("Field", typeof(int), FieldAttributes.Public); AssertExtensions.Throws("field", () => TypeBuilder.GetField(type, field)); @@ -447,36 +474,39 @@ public void SaveGenericTypeSignatureWithGenericParameter() { using (TempFile file = TempFile.Create()) { - TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder, out MethodInfo saveMethod); - GenericTypeParameterBuilder[] typeParams = tb.DefineGenericParameters(new string[] { "U", "T", "P" }); + TypeBuilder tb = CreateAssemblyAndDefineType(out AssemblyBuilder assemblyBuilder); + GenericTypeParameterBuilder[] typeParams = tb.DefineGenericParameters(["U", "T", "P"]); MethodBuilder mb = tb.DefineMethod("TestMethod", MethodAttributes.Public); - GenericTypeParameterBuilder[] methodParams = mb.DefineGenericParameters(new string[] { "M", "N" }); + GenericTypeParameterBuilder[] methodParams = mb.DefineGenericParameters(["M", "N"]); Type genericType = tb.MakeGenericType(typeParams); mb.SetReturnType(methodParams[0]); - mb.SetParameters(new Type[] { typeof(INoMethod), genericType, typeParams[1] }); + mb.SetParameters([typeof(INoMethod), genericType, typeParams[1]]); mb.GetILGenerator().Emit(OpCodes.Ret); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Type testType = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First().GetTypes()[0]; - MethodInfo testMethod = testType.GetMethod("TestMethod"); - Type paramType = testMethod.GetParameters()[1].ParameterType; - Type genericParameter = testMethod.GetParameters()[2].ParameterType; - - Assert.False(testMethod.GetParameters()[0].ParameterType.IsGenericType); - AssertGenericType("TestInterface[U,T,P]", paramType); - Assert.False(genericParameter.IsGenericType); - Assert.True(genericParameter.IsGenericParameter); - Assert.False(genericParameter.IsGenericTypeDefinition); - Assert.True(genericParameter.IsGenericTypeParameter); - Assert.False(genericParameter.IsGenericMethodParameter); - Assert.Equal("T", genericParameter.Name); - Assert.False(testMethod.ReturnType.IsGenericType); - Assert.True(testMethod.ReturnType.IsGenericParameter); - Assert.False(testMethod.ReturnType.IsGenericTypeDefinition); - Assert.False(testMethod.ReturnType.IsGenericTypeParameter); - Assert.True(testMethod.ReturnType.IsGenericMethodParameter); - Assert.Equal("M", testMethod.ReturnType.Name); + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Type testType = mlc.LoadFromAssemblyPath(file.Path).Modules.First().GetTypes()[0]; + MethodInfo testMethod = testType.GetMethod("TestMethod"); + Type paramType = testMethod.GetParameters()[1].ParameterType; + Type genericParameter = testMethod.GetParameters()[2].ParameterType; + + Assert.False(testMethod.GetParameters()[0].ParameterType.IsGenericType); + AssertGenericType("TestInterface[U,T,P]", paramType); + Assert.False(genericParameter.IsGenericType); + Assert.True(genericParameter.IsGenericParameter); + Assert.False(genericParameter.IsGenericTypeDefinition); + Assert.True(genericParameter.IsGenericTypeParameter); + Assert.False(genericParameter.IsGenericMethodParameter); + Assert.Equal("T", genericParameter.Name); + Assert.False(testMethod.ReturnType.IsGenericType); + Assert.True(testMethod.ReturnType.IsGenericParameter); + Assert.False(testMethod.ReturnType.IsGenericTypeDefinition); + Assert.False(testMethod.ReturnType.IsGenericTypeParameter); + Assert.True(testMethod.ReturnType.IsGenericMethodParameter); + Assert.Equal("M", testMethod.ReturnType.Name); + } } } @@ -485,63 +515,65 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() { using (TempFile file = TempFile.Create()) { - AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod( - s_assemblyName, null, typeof(string), out MethodInfo saveMethod); + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(s_assemblyName); ModuleBuilder mb = assemblyBuilder.DefineDynamicModule("My Module"); TypeBuilder tb = mb.DefineType("TestInterface1", TypeAttributes.Interface | TypeAttributes.Abstract); - GenericTypeParameterBuilder[] typeParams = tb.DefineGenericParameters(new string[] { "U", "T" }); - typeParams[1].SetInterfaceConstraints(new [] { typeof(INoMethod), typeof(IOneMethod) }); + GenericTypeParameterBuilder[] typeParams = tb.DefineGenericParameters(["U", "T"]); + typeParams[1].SetInterfaceConstraints([typeof(INoMethod), typeof(IOneMethod)]); MethodBuilder m11 = tb.DefineMethod("TwoParameters", MethodAttributes.Public); MethodBuilder m12 = tb.DefineMethod("FiveTypeParameters", MethodAttributes.Public); MethodBuilder m13 = tb.DefineMethod("OneParameter", MethodAttributes.Public); - m11.DefineGenericParameters(new string[] { "M", "N" }); + m11.DefineGenericParameters(["M", "N"]); m11.GetILGenerator().Emit(OpCodes.Ret); - GenericTypeParameterBuilder[] methodParams = m12.DefineGenericParameters(new string[] { "A", "B", "C", "D", "F" }); + GenericTypeParameterBuilder[] methodParams = m12.DefineGenericParameters(["A", "B", "C", "D", "F"]); m12.GetILGenerator().Emit(OpCodes.Ret); - methodParams[2].SetInterfaceConstraints(new [] { typeof(IMultipleMethod) }); - m13.DefineGenericParameters(new string[] { "T" }); + methodParams[2].SetInterfaceConstraints([typeof(IMultipleMethod)]); + m13.DefineGenericParameters(["T"]); m13.GetILGenerator().Emit(OpCodes.Ret); TypeBuilder tb2 = mb.DefineType("TestInterface2", TypeAttributes.Interface | TypeAttributes.Abstract); - tb2.DefineGenericParameters(new string[] { "TFirst", "TSecond", "TThird" }); + tb2.DefineGenericParameters(["TFirst", "TSecond", "TThird"]); MethodBuilder m21 = tb2.DefineMethod("TestMethod", MethodAttributes.Public); - m21.DefineGenericParameters(new string[] { "X", "Y", "Z" }); + m21.DefineGenericParameters(["X", "Y", "Z"]); m21.GetILGenerator().Emit(OpCodes.Ret); TypeBuilder tb3 = mb.DefineType("TestType"); - GenericTypeParameterBuilder[] typePar = tb3.DefineGenericParameters(new string[] { "TOne" }); + GenericTypeParameterBuilder[] typePar = tb3.DefineGenericParameters(["TOne"]); typePar[0].SetBaseTypeConstraint(typeof(EmptyTestClass)); tb3.CreateType(); tb2.CreateType(); tb.CreateType(); - saveMethod.Invoke(assemblyBuilder, new object[] { file.Path }); - - Module m = AssemblySaveTools.LoadAssemblyFromPath(file.Path).Modules.First(); - Type[] type1Params = m.GetTypes()[0].GetGenericArguments(); - Type[] type2Params = m.GetTypes()[1].GetGenericArguments(); - Type[] type3Params = m.GetTypes()[2].GetGenericArguments(); - - Assert.Equal("U", type1Params[0].Name); - Assert.Empty(type1Params[0].GetTypeInfo().GetGenericParameterConstraints()); - Assert.Equal("T", type1Params[1].Name); - Assert.Equal(nameof(IOneMethod), type1Params[1].GetTypeInfo().GetGenericParameterConstraints()[1].Name); - Assert.Equal("TFirst", type2Params[0].Name); - Assert.Equal("TSecond", type2Params[1].Name); - Assert.Equal("TThird", type2Params[2].Name); - Assert.Equal("TOne", type3Params[0].Name); - Assert.Equal(nameof(EmptyTestClass), type3Params[0].GetTypeInfo().GetGenericParameterConstraints()[0].Name); - - Type[] method11Params = m.GetTypes()[0].GetMethod("TwoParameters").GetGenericArguments(); - Type[] method12Params = m.GetTypes()[0].GetMethod("FiveTypeParameters").GetGenericArguments(); - Assert.Equal(nameof(IMultipleMethod), method12Params[2].GetTypeInfo().GetGenericParameterConstraints()[0].Name); - Type[] method13Params = m.GetTypes()[0].GetMethod("OneParameter").GetGenericArguments(); - Type[] method21Params = m.GetTypes()[1].GetMethod("TestMethod").GetGenericArguments(); - - Assert.Equal("M", method11Params[0].Name); - Assert.Equal("N", method11Params[1].Name); - Assert.Equal("A", method12Params[0].Name); - Assert.Equal("F", method12Params[4].Name); - Assert.Equal("T", method13Params[0].Name); - Assert.Equal("X", method21Params[0].Name); - Assert.Equal("Z", method21Params[2].Name); + assemblyBuilder.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Module m = mlc.LoadFromAssemblyPath(file.Path).Modules.First(); + Type[] type1Params = m.GetTypes()[0].GetGenericArguments(); + Type[] type2Params = m.GetTypes()[1].GetGenericArguments(); + Type[] type3Params = m.GetTypes()[2].GetGenericArguments(); + + Assert.Equal("U", type1Params[0].Name); + Assert.Empty(type1Params[0].GetTypeInfo().GetGenericParameterConstraints()); + Assert.Equal("T", type1Params[1].Name); + Assert.Equal(nameof(IOneMethod), type1Params[1].GetTypeInfo().GetGenericParameterConstraints()[1].Name); + Assert.Equal("TFirst", type2Params[0].Name); + Assert.Equal("TSecond", type2Params[1].Name); + Assert.Equal("TThird", type2Params[2].Name); + Assert.Equal("TOne", type3Params[0].Name); + Assert.Equal(nameof(EmptyTestClass), type3Params[0].GetTypeInfo().GetGenericParameterConstraints()[0].Name); + + Type[] method11Params = m.GetTypes()[0].GetMethod("TwoParameters").GetGenericArguments(); + Type[] method12Params = m.GetTypes()[0].GetMethod("FiveTypeParameters").GetGenericArguments(); + Assert.Equal(nameof(IMultipleMethod), method12Params[2].GetTypeInfo().GetGenericParameterConstraints()[0].Name); + Type[] method13Params = m.GetTypes()[0].GetMethod("OneParameter").GetGenericArguments(); + Type[] method21Params = m.GetTypes()[1].GetMethod("TestMethod").GetGenericArguments(); + + Assert.Equal("M", method11Params[0].Name); + Assert.Equal("N", method11Params[1].Name); + Assert.Equal("A", method12Params[0].Name); + Assert.Equal("F", method12Params[4].Name); + Assert.Equal("T", method13Params[0].Name); + Assert.Equal("X", method21Params[0].Name); + Assert.Equal("Z", method21Params[2].Name); + } } } } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs index ff75e5d6534182..2e5dc74f733dce 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs @@ -27,7 +27,6 @@ internal sealed class RegexAssemblyCompiler : RegexCompiler private readonly AssemblyBuilder _assembly; private readonly ModuleBuilder _module; - private readonly MethodInfo _save; internal RegexAssemblyCompiler(AssemblyName an, CustomAttributeBuilder[]? attribs, string? resourceFile) { @@ -37,16 +36,8 @@ internal RegexAssemblyCompiler(AssemblyName an, CustomAttributeBuilder[]? attrib throw new PlatformNotSupportedException(); } - // TODO: Use public API when it's available: https://github.com/dotnet/runtime/issues/15704 - Type abType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!; - MethodInfo defineDynamicAssembly = abType.GetMethod("DefinePersistedAssembly", - BindingFlags.NonPublic | BindingFlags.Static, - [typeof(AssemblyName), typeof(Assembly), typeof(List)]) ?? - throw new InvalidOperationException("Could not find method AssemblyBuilderImpl.DefinePersistedAssembly"); - _assembly = (AssemblyBuilder?)defineDynamicAssembly.Invoke(null, [an, typeof(object).Assembly, attribs is not null ? new List(attribs) : null]) ?? + _assembly = AssemblyBuilder.DefinePersistedAssembly(an, typeof(object).Assembly, attribs is not null ? new List(attribs) : null) ?? throw new InvalidOperationException("DefinePersistedAssembly returned null"); - _save = abType.GetMethod("Save", BindingFlags.NonPublic | BindingFlags.Instance, [typeof(string)]) ?? - throw new InvalidOperationException("Could not find method AssemblyBuilderImpl.Save"); _module = _assembly.DefineDynamicModule(an.Name + ".dll"); } @@ -242,7 +233,7 @@ internal void Save(string fileName) fileName += ".dll"; } - _save.Invoke(_assembly, [fileName]); // TODO: Use public API when it's available: https://github.com/dotnet/runtime/issues/15704 + _assembly.Save(fileName); } /// Begins the definition of a new type with a specified base class diff --git a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml index 05317adadabbfe..2f7dfb841c805b 100644 --- a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml +++ b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml @@ -1,4 +1,4 @@  - + \ No newline at end of file From ff1eeff500e9a2eef7c593fc2517e9f8d0c40fc6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Sat, 20 Jan 2024 22:04:28 -0800 Subject: [PATCH 150/189] Update TestsManifestGeneration.Etw.cs (#97258) * Update TestsManifestGeneration.Etw.cs Disable tests on x86 --- .../tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs index 4973ad18f74aab..bc1b3c7cba8c21 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsManifestGeneration.Etw.cs @@ -31,6 +31,7 @@ public partial class TestsManifestGeneration /// ETW only works with elevated process [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServerAndRemoteExecutorSupported))] [SkipOnCoreClr("Test should only be run in non-stress modes", ~RuntimeTestModes.RegularRun)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97255", typeof(PlatformDetection), nameof(PlatformDetection.IsX86Process))] public void Test_EventSource_EtwManifestGeneration() { var pid = Process.GetCurrentProcess().Id; @@ -71,6 +72,7 @@ public void Test_EventSource_EtwManifestGeneration() [ConditionalFact(nameof(IsProcessElevatedAndNotWindowsNanoServerAndRemoteExecutorSupported))] [SkipOnCoreClr("Test should only be run in non-stress modes", ~RuntimeTestModes.RegularRun)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97255", typeof(PlatformDetection), nameof(PlatformDetection.IsX86Process))] public void Test_EventSource_EtwManifestGenerationRollover() { var pid = Process.GetCurrentProcess().Id; From 756a138766b5bc376dc704f57bf8abbc53104d90 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sun, 21 Jan 2024 11:39:46 +0100 Subject: [PATCH 151/189] [browser][MT] Use auto thread dispatch in HTTP (#95370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: campersau Co-authored-by: Marek Fišera --- .gitattributes | 2 +- .../NetCoreServer/Handlers/EchoHandler.cs | 20 +- .../BrowserHttpHandler/BrowserHttpHandler.cs | 522 +++++++----------- .../BrowserHttpHandler/BrowserHttpInterop.cs | 106 ++-- .../src/System/Net/Http/CancellationHelper.cs | 10 +- .../System.Net.Http.Functional.Tests.csproj | 2 - .../XmlSchemaSet/TC_SchemaSet_Add_URL.cs | 1 - .../src/CompatibilitySuppressions.xml | 12 - ....Runtime.InteropServices.JavaScript.csproj | 1 - .../InteropServices/JavaScript/JSHost.cs | 12 - .../InteropServices/JavaScript/JSWebWorker.cs | 97 ++-- .../Marshaling/JSMarshalerArgument.Task.cs | 4 + .../SynchronizationContextExtensions.cs | 152 ----- ...me.InteropServices.JavaScript.Tests.csproj | 2 + .../JavaScript/WebWorkerTest.cs | 157 +++++- src/libraries/tests.proj | 2 +- src/mono/browser/runtime/dotnet.d.ts | 6 +- src/mono/browser/runtime/exports-internal.ts | 8 +- src/mono/browser/runtime/http.ts | 208 ++++--- src/mono/browser/runtime/loader/logging.ts | 6 +- 20 files changed, 592 insertions(+), 738 deletions(-) delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SynchronizationContextExtensions.cs diff --git a/.gitattributes b/.gitattributes index 55a35b1afffff0..2aa4cb2e2a9e2b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -77,4 +77,4 @@ src/tests/JIT/Performance/CodeQuality/BenchmarksGame/reverse-complement/revcomp- src/tests/JIT/Performance/CodeQuality/BenchmarksGame/reverse-complement/revcomp-input25000.txt text eol=lf src/tests/JIT/Performance/CodeQuality/BenchmarksGame/k-nucleotide/knucleotide-input.txt text eol=lf src/tests/JIT/Performance/CodeQuality/BenchmarksGame/k-nucleotide/knucleotide-input-big.txt text eol=lf -src/mono/wasm/runtime/dotnet.d.ts text eol=lf +src/mono/browser/runtime/dotnet.d.ts text eol=lf diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoHandler.cs b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoHandler.cs index fd05cff102d2e6..6888c57e11285b 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoHandler.cs +++ b/src/libraries/Common/tests/System/Net/Prerequisites/NetCoreServer/Handlers/EchoHandler.cs @@ -4,6 +4,7 @@ using System; using System.Security.Cryptography; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -27,18 +28,33 @@ public static async Task InvokeAsync(HttpContext context) RequestInformation info = await RequestInformation.CreateAsync(context.Request); string echoJson = info.SerializeToJson(); + byte[] bytes = Encoding.UTF8.GetBytes(echoJson); + // Compute MD5 hash so that clients can verify the received data. using (MD5 md5 = MD5.Create()) { - byte[] bytes = Encoding.UTF8.GetBytes(echoJson); byte[] hash = md5.ComputeHash(bytes); string encodedHash = Convert.ToBase64String(hash); context.Response.Headers["Content-MD5"] = encodedHash; context.Response.ContentType = "application/json"; context.Response.ContentLength = bytes.Length; - await context.Response.Body.WriteAsync(bytes, 0, bytes.Length); } + + if (context.Request.QueryString.HasValue && context.Request.QueryString.Value.Contains("delay10sec")) + { + await context.Response.StartAsync(CancellationToken.None); + await context.Response.Body.FlushAsync(); + + await Task.Delay(10000); + } + else if (context.Request.QueryString.HasValue && context.Request.QueryString.Value.Contains("delay1sec")) + { + await context.Response.StartAsync(CancellationToken.None); + await Task.Delay(1000); + } + + await context.Response.Body.WriteAsync(bytes, 0, bytes.Length); } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 218a51b441fe8a..bbcd625d036d6a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.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; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -15,9 +16,6 @@ namespace System.Net.Http // the JavaScript objects have thread affinity, it is necessary that the continuations run the same thread as the start of the async method. internal sealed class BrowserHttpHandler : HttpMessageHandler { - private static readonly HttpRequestOptionsKey EnableStreamingRequest = new HttpRequestOptionsKey("WebAssemblyEnableStreamingRequest"); - private static readonly HttpRequestOptionsKey EnableStreamingResponse = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); - private static readonly HttpRequestOptionsKey> FetchOptions = new HttpRequestOptionsKey>("WebAssemblyFetchOptions"); private bool _allowAutoRedirect = HttpHandlerDefaults.DefaultAutomaticRedirection; // flag to determine if the _allowAutoRedirect was explicitly set or not. private bool _isAllowAutoRedirectTouched; @@ -114,71 +112,112 @@ public bool AllowAutoRedirect public const bool SupportsProxy = false; public const bool SupportsRedirectConfiguration = true; -#if FEATURE_WASM_THREADS - private ConcurrentDictionary? _properties; - public IDictionary Properties => _properties ??= new ConcurrentDictionary(); -#else private Dictionary? _properties; public IDictionary Properties => _properties ??= new Dictionary(); -#endif protected internal override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) { throw new PlatformNotSupportedException(); } - private static async Task CallFetch(HttpRequestMessage request, CancellationToken cancellationToken, bool? allowAutoRedirect) + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - int headerCount = request.Headers.Count + request.Content?.Headers.Count ?? 0; - List headerNames = new List(headerCount); - List headerValues = new List(headerCount); - JSObject abortController = BrowserHttpInterop.CreateAbortController(); - CancellationTokenRegistration abortRegistration = cancellationToken.Register(static s => + bool? allowAutoRedirect = _isAllowAutoRedirectTouched ? AllowAutoRedirect : null; + var controller = new BrowserHttpController(request, allowAutoRedirect, cancellationToken); + return controller.CallFetch(); + } + } + + internal sealed class BrowserHttpController : IDisposable + { + private static readonly HttpRequestOptionsKey EnableStreamingRequest = new HttpRequestOptionsKey("WebAssemblyEnableStreamingRequest"); + private static readonly HttpRequestOptionsKey EnableStreamingResponse = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); + private static readonly HttpRequestOptionsKey> FetchOptions = new HttpRequestOptionsKey>("WebAssemblyFetchOptions"); + + internal readonly JSObject _jsController; + private readonly CancellationTokenRegistration _abortRegistration; + private readonly string[] _optionNames; + private readonly object?[] _optionValues; + private readonly string[] _headerNames; + private readonly string[] _headerValues; + private readonly string uri; + private readonly CancellationToken _cancellationToken; + private readonly HttpRequestMessage _request; + private bool _isDisposed; + + public BrowserHttpController(HttpRequestMessage request, bool? allowAutoRedirect, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(request); + if (request.RequestUri == null) { - JSObject _abortController = (JSObject)s!; -#if FEATURE_WASM_THREADS - if (!_abortController.IsDisposed) - { - _abortController.SynchronizationContext.Send(static (JSObject __abortController) => - { - BrowserHttpInterop.AbortRequest(__abortController); - __abortController.Dispose(); - }, _abortController); - } -#else - if (!_abortController.IsDisposed) - { - BrowserHttpInterop.AbortRequest(_abortController); - _abortController.Dispose(); - } -#endif - }, abortController); - try + throw new ArgumentNullException(nameof(request.RequestUri)); + } + + _cancellationToken = cancellationToken; + _request = request; + + JSObject httpController = BrowserHttpInterop.CreateController(); + CancellationTokenRegistration abortRegistration = cancellationToken.Register(static s => { - if (request.RequestUri == null) + JSObject _httpController = (JSObject)s!; + + if (!_httpController.IsDisposed) { - throw new ArgumentNullException(nameof(request.RequestUri)); + BrowserHttpInterop.AbortRequest(_httpController); } + }, httpController); + + + _jsController = httpController; + _abortRegistration = abortRegistration; + + uri = request.RequestUri.IsAbsoluteUri ? request.RequestUri.AbsoluteUri : request.RequestUri.ToString(); - string uri = request.RequestUri.IsAbsoluteUri ? request.RequestUri.AbsoluteUri : request.RequestUri.ToString(); + bool hasFetchOptions = request.Options.TryGetValue(FetchOptions, out IDictionary? fetchOptions); + int optionCount = 1 + (allowAutoRedirect.HasValue ? 1 : 0) + (hasFetchOptions && fetchOptions != null ? fetchOptions.Count : 0); + int optionIndex = 0; - bool hasFetchOptions = request.Options.TryGetValue(FetchOptions, out IDictionary? fetchOptions); - int optionCount = 1 + (allowAutoRedirect.HasValue ? 1 : 0) + (hasFetchOptions && fetchOptions != null ? fetchOptions.Count : 0); - int optionIndex = 0; - string[] optionNames = new string[optionCount]; - object?[] optionValues = new object?[optionCount]; + // note there could be more values for each header name and so this is just name count + int headerCount = request.Headers.Count + (request.Content?.Headers.Count ?? 0); - optionNames[optionIndex] = "method"; - optionValues[optionIndex] = request.Method.Method; + _optionNames = new string[optionCount]; + _optionValues = new object?[optionCount]; + + _optionNames[optionIndex] = "method"; + _optionValues[optionIndex] = request.Method.Method; + optionIndex++; + if (allowAutoRedirect.HasValue) + { + _optionNames[optionIndex] = "redirect"; + _optionValues[optionIndex] = allowAutoRedirect.Value ? "follow" : "manual"; optionIndex++; - if (allowAutoRedirect.HasValue) + } + + if (hasFetchOptions && fetchOptions != null) + { + foreach (KeyValuePair item in fetchOptions) { - optionNames[optionIndex] = "redirect"; - optionValues[optionIndex] = allowAutoRedirect.Value ? "follow" : "manual"; + _optionNames[optionIndex] = item.Key; + _optionValues[optionIndex] = item.Value; optionIndex++; } + } + + var headerNames = new List(headerCount); + var headerValues = new List(headerCount); + + foreach (KeyValuePair> header in request.Headers) + { + foreach (string value in header.Value) + { + headerNames.Add(header.Key); + headerValues.Add(value); + } + } - foreach (KeyValuePair> header in request.Headers) + if (request.Content != null) + { + foreach (KeyValuePair> header in request.Content.Headers) { foreach (string value in header.Value) { @@ -186,117 +225,79 @@ private static async Task CallFetch(HttpRequestMessage reques headerValues.Add(value); } } + } + _headerNames = headerNames.ToArray(); + _headerValues = headerValues.ToArray(); + } - if (request.Content != null) - { - foreach (KeyValuePair> header in request.Content.Headers) - { - foreach (string value in header.Value) - { - headerNames.Add(header.Key); - headerValues.Add(value); - } - } - } + public async Task CallFetch() + { + CancellationHelper.ThrowIfCancellationRequested(_cancellationToken); - if (hasFetchOptions && fetchOptions != null) - { - foreach (KeyValuePair item in fetchOptions) - { - optionNames[optionIndex] = item.Key; - optionValues[optionIndex] = item.Value; - optionIndex++; - } - } + BrowserHttpWriteStream? writeStream = null; + Task fetchPromise; + bool streamingRequestEnabled = false; - JSObject? fetchResponse; - cancellationToken.ThrowIfCancellationRequested(); - if (request.Content != null) + try + { + if (_request.Content != null) { - bool streamingEnabled = false; if (BrowserHttpInterop.SupportsStreamingRequest()) { - request.Options.TryGetValue(EnableStreamingRequest, out streamingEnabled); + _request.Options.TryGetValue(EnableStreamingRequest, out streamingRequestEnabled); } - if (streamingEnabled) + if (streamingRequestEnabled) { - using (JSObject transformStream = BrowserHttpInterop.CreateTransformStream()) - { - Task fetchPromise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, transformStream); - Task fetchTask = BrowserHttpInterop.CancelationHelper(fetchPromise, cancellationToken).AsTask(); // initialize fetch cancellation - - using (WasmHttpWriteStream stream = new WasmHttpWriteStream(transformStream)) - { - try - { - await request.Content.CopyToAsync(stream, cancellationToken).ConfigureAwait(true); - Task closePromise = BrowserHttpInterop.TransformStreamClose(transformStream); - await BrowserHttpInterop.CancelationHelper(closePromise, cancellationToken).ConfigureAwait(true); - } - catch (Exception) - { - BrowserHttpInterop.TransformStreamAbort(transformStream); - if (!abortController.IsDisposed) - { - BrowserHttpInterop.AbortRequest(abortController); - } - try - { - using (fetchResponse = await fetchTask.ConfigureAwait(true)) // observe exception - { - BrowserHttpInterop.AbortResponse(fetchResponse); - } - } - catch { /* ignore */ } - cancellationToken.ThrowIfCancellationRequested(); - throw; - } - } - - fetchResponse = await fetchTask.ConfigureAwait(true); - } + fetchPromise = BrowserHttpInterop.FetchStream(_jsController, uri, _headerNames, _headerValues, _optionNames, _optionValues); + writeStream = new BrowserHttpWriteStream(this); + await _request.Content.CopyToAsync(writeStream, _cancellationToken).ConfigureAwait(false); + var closePromise = BrowserHttpInterop.TransformStreamClose(_jsController); + await BrowserHttpInterop.CancellationHelper(closePromise, _cancellationToken, _jsController).ConfigureAwait(false); } else { - byte[] buffer = await request.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(true); - cancellationToken.ThrowIfCancellationRequested(); + byte[] buffer = await _request.Content.ReadAsByteArrayAsync(_cancellationToken).ConfigureAwait(false); + CancellationHelper.ThrowIfCancellationRequested(_cancellationToken); - Task fetchPromise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController, buffer); - fetchResponse = await BrowserHttpInterop.CancelationHelper(fetchPromise, cancellationToken).ConfigureAwait(true); + Memory bufferMemory = buffer.AsMemory(); + // http_wasm_fetch_byte makes a copy of the bytes synchronously, so we can un-pin it synchronously + using MemoryHandle pinBuffer = bufferMemory.Pin(); + fetchPromise = BrowserHttpInterop.FetchBytes(_jsController, uri, _headerNames, _headerValues, _optionNames, _optionValues, pinBuffer, buffer.Length); } } else { - Task fetchPromise = BrowserHttpInterop.Fetch(uri, headerNames.ToArray(), headerValues.ToArray(), optionNames, optionValues, abortController); - fetchResponse = await BrowserHttpInterop.CancelationHelper(fetchPromise, cancellationToken).ConfigureAwait(true); + fetchPromise = BrowserHttpInterop.Fetch(_jsController, uri, _headerNames, _headerValues, _optionNames, _optionValues); } + await BrowserHttpInterop.CancellationHelper(fetchPromise, _cancellationToken, _jsController).ConfigureAwait(false); - return new WasmFetchResponse(fetchResponse, abortController, abortRegistration); + return ConvertResponse(); } catch (Exception ex) { - abortRegistration.Dispose(); - abortController.Dispose(); + Dispose(); // will also abort request if (ex is JSException jse) { throw new HttpRequestException(jse.Message, jse); } throw; } + finally + { + writeStream?.Dispose(); + } } - private static HttpResponseMessage ConvertResponse(HttpRequestMessage request, WasmFetchResponse fetchResponse) + private HttpResponseMessage ConvertResponse() { -#if FEATURE_WASM_THREADS - lock (fetchResponse.ThisLock) + lock (this) { -#endif - fetchResponse.ThrowIfDisposed(); - string? responseType = fetchResponse.FetchResponse!.GetPropertyAsString("type")!; - int status = fetchResponse.FetchResponse.GetPropertyAsInt32("status"); + ThrowIfDisposed(); + string? responseType = BrowserHttpInterop.GetResponseType(_jsController); + int status = BrowserHttpInterop.GetResponseStatus(_jsController); HttpResponseMessage responseMessage = new HttpResponseMessage((HttpStatusCode)status); - responseMessage.RequestMessage = request; + responseMessage.RequestMessage = _request; if (responseType == "opaqueredirect") { // Here we will set the ReasonPhrase so that it can be evaluated later. @@ -309,77 +310,69 @@ private static HttpResponseMessage ConvertResponse(HttpRequestMessage request, W responseMessage.SetReasonPhraseWithoutValidation(responseType); } - bool streamingEnabled = false; + bool streamingResponseEnabled = false; if (BrowserHttpInterop.SupportsStreamingResponse()) { - request.Options.TryGetValue(EnableStreamingResponse, out streamingEnabled); + _request.Options.TryGetValue(EnableStreamingResponse, out streamingResponseEnabled); } - responseMessage.Content = streamingEnabled - ? new StreamContent(new WasmHttpReadStream(fetchResponse)) - : new BrowserHttpContent(fetchResponse); - + responseMessage.Content = streamingResponseEnabled + ? new StreamContent(new BrowserHttpReadStream(this)) + : new BrowserHttpContent(this); - // Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation - // CORS will only allow access to certain headers on browser. - BrowserHttpInterop.GetResponseHeaders(fetchResponse.FetchResponse, responseMessage.Headers, responseMessage.Content.Headers); + BrowserHttpInterop.GetResponseHeaders(_jsController!, responseMessage.Headers, responseMessage.Content.Headers); return responseMessage; -#if FEATURE_WASM_THREADS } //lock -#endif } - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + public void ThrowIfDisposed() { - bool? allowAutoRedirect = _isAllowAutoRedirectTouched ? AllowAutoRedirect : null; -#if FEATURE_WASM_THREADS - return JSHost.CurrentOrMainJSSynchronizationContext.Post(() => + lock (this) { -#endif - return Impl(request, cancellationToken, allowAutoRedirect); -#if FEATURE_WASM_THREADS - }); -#endif + ObjectDisposedException.ThrowIf(_isDisposed, this); + } //lock + } - static async Task Impl(HttpRequestMessage request, CancellationToken cancellationToken, bool? allowAutoRedirect) + public void Dispose() + { + lock (this) { - WasmFetchResponse fetchRespose = await CallFetch(request, cancellationToken, allowAutoRedirect).ConfigureAwait(true); - return ConvertResponse(request, fetchRespose); + if (_isDisposed) + return; + _isDisposed = true; + } + _abortRegistration.Dispose(); + if (_jsController != null) + { + if (!_jsController.IsDisposed) + { + BrowserHttpInterop.AbortRequest(_jsController);// aborts also response + } + _jsController.Dispose(); } } } - internal sealed class WasmHttpWriteStream : Stream + internal sealed class BrowserHttpWriteStream : Stream { - private readonly JSObject _transformStream; - - public WasmHttpWriteStream(JSObject transformStream) + private readonly BrowserHttpController _controller; // we don't own it, we don't dispose it from here + public BrowserHttpWriteStream(BrowserHttpController controller) { - ArgumentNullException.ThrowIfNull(transformStream); + ArgumentNullException.ThrowIfNull(controller); - _transformStream = transformStream; + _controller = controller; } private Task WriteAsyncCore(ReadOnlyMemory buffer, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); -#if FEATURE_WASM_THREADS - return _transformStream.SynchronizationContext.Post(() => Impl(this, buffer, cancellationToken)); -#else - return Impl(this, buffer, cancellationToken); -#endif - static async Task Impl(WasmHttpWriteStream self, ReadOnlyMemory buffer, CancellationToken cancellationToken) - { - using (Buffers.MemoryHandle handle = buffer.Pin()) - { - Task writePromise = TransformStreamWriteUnsafe(self._transformStream, buffer, handle); - await BrowserHttpInterop.CancelationHelper(writePromise, cancellationToken).ConfigureAwait(true); - } - } + CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + _controller.ThrowIfDisposed(); - static unsafe Task TransformStreamWriteUnsafe(JSObject transformStream, ReadOnlyMemory buffer, Buffers.MemoryHandle handle) - => BrowserHttpInterop.TransformStreamWrite(transformStream, (nint)handle.Pointer, buffer.Length); + // http_wasm_transform_stream_write makes a copy of the bytes synchronously, so we can dispose the handle synchronously + using MemoryHandle pinBuffer = buffer.Pin(); + Task writePromise = BrowserHttpInterop.TransformStreamWriteUnsafe(_controller._jsController, buffer, pinBuffer); + return BrowserHttpInterop.CancellationHelper(writePromise, cancellationToken, _controller._jsController); } public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) @@ -399,7 +392,6 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati protected override void Dispose(bool disposing) { - _transformStream.Dispose(); } public override void Flush() @@ -436,159 +428,57 @@ public override void Write(byte[] buffer, int offset, int count) #endregion } - internal sealed class WasmFetchResponse : IDisposable - { -#if FEATURE_WASM_THREADS - public readonly object ThisLock = new object(); -#endif - public JSObject? FetchResponse; - private readonly JSObject _abortController; - private readonly CancellationTokenRegistration _abortRegistration; - private bool _isDisposed; - - public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenRegistration abortRegistration) - { - ArgumentNullException.ThrowIfNull(fetchResponse); - ArgumentNullException.ThrowIfNull(abortController); - - FetchResponse = fetchResponse; - _abortRegistration = abortRegistration; - _abortController = abortController; - } - - public void ThrowIfDisposed() - { -#if FEATURE_WASM_THREADS - lock (ThisLock) - { -#endif - ObjectDisposedException.ThrowIf(_isDisposed, this); -#if FEATURE_WASM_THREADS - } //lock -#endif - } - - public void Dispose() - { - if (_isDisposed) - return; - -#if FEATURE_WASM_THREADS - FetchResponse?.SynchronizationContext.Post(static (WasmFetchResponse self) => - { - lock (self.ThisLock) - { - if (!self._isDisposed) - { - self._isDisposed = true; - self._abortRegistration.Dispose(); - self._abortController.Dispose(); - if (!self.FetchResponse!.IsDisposed) - { - BrowserHttpInterop.AbortResponse(self.FetchResponse); - } - self.FetchResponse.Dispose(); - self.FetchResponse = null; - } - return Task.CompletedTask; - } - }, this); -#else - _isDisposed = true; - _abortRegistration.Dispose(); - _abortController.Dispose(); - if (FetchResponse != null) - { - if (!FetchResponse.IsDisposed) - { - BrowserHttpInterop.AbortResponse(FetchResponse); - } - FetchResponse.Dispose(); - FetchResponse = null; - } -#endif - } - } - internal sealed class BrowserHttpContent : HttpContent { private byte[]? _data; private int _length = -1; - private readonly WasmFetchResponse _fetchResponse; + private readonly BrowserHttpController _controller; - public BrowserHttpContent(WasmFetchResponse fetchResponse) + public BrowserHttpContent(BrowserHttpController controller) { - ArgumentNullException.ThrowIfNull(fetchResponse); - _fetchResponse = fetchResponse; + ArgumentNullException.ThrowIfNull(controller); + _controller = controller; } // TODO allocate smaller buffer and call multiple times private async ValueTask GetResponseData(CancellationToken cancellationToken) { Task promise; -#if FEATURE_WASM_THREADS - lock (_fetchResponse.ThisLock) + lock (_controller) { -#endif if (_data != null) { return _data; } - _fetchResponse.ThrowIfDisposed(); - promise = BrowserHttpInterop.GetResponseLength(_fetchResponse.FetchResponse!); -#if FEATURE_WASM_THREADS + _controller.ThrowIfDisposed(); + promise = BrowserHttpInterop.GetResponseLength(_controller._jsController!); } //lock -#endif - _length = await BrowserHttpInterop.CancelationHelper(promise, cancellationToken, _fetchResponse.FetchResponse).ConfigureAwait(true); -#if FEATURE_WASM_THREADS - lock (_fetchResponse.ThisLock) + _length = await BrowserHttpInterop.CancellationHelper(promise, cancellationToken, _controller._jsController).ConfigureAwait(false); + lock (_controller) { -#endif _data = new byte[_length]; - BrowserHttpInterop.GetResponseBytes(_fetchResponse.FetchResponse!, new Span(_data)); + BrowserHttpInterop.GetResponseBytes(_controller._jsController!, new Span(_data)); return _data; -#if FEATURE_WASM_THREADS } //lock -#endif } - protected override Task CreateContentReadStreamAsync() + protected override async Task CreateContentReadStreamAsync() { - _fetchResponse.ThrowIfDisposed(); -#if FEATURE_WASM_THREADS - return _fetchResponse.FetchResponse!.SynchronizationContext.Post(() => Impl(this)); -#else - return Impl(this); -#endif - static async Task Impl(BrowserHttpContent self) - { - self._fetchResponse.ThrowIfDisposed(); - byte[] data = await self.GetResponseData(CancellationToken.None).ConfigureAwait(true); - return new MemoryStream(data, writable: false); - } + byte[] data = await GetResponseData(CancellationToken.None).ConfigureAwait(false); + return new MemoryStream(data, writable: false); } protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) => SerializeToStreamAsync(stream, context, CancellationToken.None); - protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) + protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(stream, nameof(stream)); - _fetchResponse.ThrowIfDisposed(); -#if FEATURE_WASM_THREADS - return _fetchResponse.FetchResponse!.SynchronizationContext.Post(() => Impl(this, stream, cancellationToken)); -#else - return Impl(this, stream, cancellationToken); -#endif - - static async Task Impl(BrowserHttpContent self, Stream stream, CancellationToken cancellationToken) - { - self._fetchResponse.ThrowIfDisposed(); - byte[] data = await self.GetResponseData(cancellationToken).ConfigureAwait(true); - await stream.WriteAsync(data, cancellationToken).ConfigureAwait(true); - } + + byte[] data = await GetResponseData(cancellationToken).ConfigureAwait(false); + await stream.WriteAsync(data, cancellationToken).ConfigureAwait(false); } protected internal override bool TryComputeLength(out long length) @@ -605,52 +495,40 @@ protected internal override bool TryComputeLength(out long length) protected override void Dispose(bool disposing) { - _fetchResponse.Dispose(); + _controller.Dispose(); base.Dispose(disposing); } } - internal sealed class WasmHttpReadStream : Stream + internal sealed class BrowserHttpReadStream : Stream { - private WasmFetchResponse _fetchResponse; + private BrowserHttpController _controller; // we own the object and have to dispose it - public WasmHttpReadStream(WasmFetchResponse fetchResponse) + public BrowserHttpReadStream(BrowserHttpController controller) { - _fetchResponse = fetchResponse; + _controller = controller; } public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(buffer, nameof(buffer)); - _fetchResponse.ThrowIfDisposed(); -#if FEATURE_WASM_THREADS - return await _fetchResponse.FetchResponse!.SynchronizationContext.Post(() => Impl(this, buffer, cancellationToken)).ConfigureAwait(true); -#else - return await Impl(this, buffer, cancellationToken).ConfigureAwait(true); -#endif - - static async Task Impl(WasmHttpReadStream self, Memory buffer, CancellationToken cancellationToken) + _controller.ThrowIfDisposed(); + + MemoryHandle pinBuffer = buffer.Pin(); + int bytesCount; + try { - self._fetchResponse.ThrowIfDisposed(); - Task promise; - using (Buffers.MemoryHandle handle = buffer.Pin()) - { -#if FEATURE_WASM_THREADS - lock (self._fetchResponse.ThisLock) - { -#endif - self._fetchResponse.ThrowIfDisposed(); - promise = GetStreamedResponseBytesUnsafe(self._fetchResponse, buffer, handle); -#if FEATURE_WASM_THREADS - } //lock -#endif - int response = await BrowserHttpInterop.CancelationHelper(promise, cancellationToken, self._fetchResponse.FetchResponse).ConfigureAwait(true); - return response; - } + _controller.ThrowIfDisposed(); - unsafe static Task GetStreamedResponseBytesUnsafe(WasmFetchResponse _fetchResponse, Memory buffer, Buffers.MemoryHandle handle) - => BrowserHttpInterop.GetStreamedResponseBytes(_fetchResponse.FetchResponse!, (IntPtr)handle.Pointer, buffer.Length); + var promise = BrowserHttpInterop.GetStreamedResponseBytesUnsafe(_controller._jsController, buffer, pinBuffer); + bytesCount = await BrowserHttpInterop.CancellationHelper(promise, cancellationToken, _controller._jsController).ConfigureAwait(false); + } + finally + { + // this must be after await, because http_wasm_get_streamed_response_bytes is using the buffer in a continuation + pinBuffer.Dispose(); } + return bytesCount; } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -665,7 +543,7 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel protected override void Dispose(bool disposing) { - _fetchResponse.Dispose(); + _controller.Dispose(); } public override void Flush() diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs index b942bccd4c760a..c37be3fc11f2bf 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpInterop.cs @@ -1,7 +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.IO; +using System.Buffers; using System.Net.Http.Headers; using System.Runtime.InteropServices.JavaScript; using System.Threading; @@ -17,47 +17,53 @@ internal static partial class BrowserHttpInterop [JSImport("INTERNAL.http_wasm_supports_streaming_response")] public static partial bool SupportsStreamingResponse(); - [JSImport("INTERNAL.http_wasm_create_abort_controler")] - public static partial JSObject CreateAbortController(); + [JSImport("INTERNAL.http_wasm_create_controller")] + public static partial JSObject CreateController(); [JSImport("INTERNAL.http_wasm_abort_request")] public static partial void AbortRequest( - JSObject abortController); + JSObject httpController); [JSImport("INTERNAL.http_wasm_abort_response")] public static partial void AbortResponse( - JSObject fetchResponse); - - [JSImport("INTERNAL.http_wasm_create_transform_stream")] - public static partial JSObject CreateTransformStream(); + JSObject httpController); [JSImport("INTERNAL.http_wasm_transform_stream_write")] public static partial Task TransformStreamWrite( - JSObject transformStream, + JSObject httpController, IntPtr bufferPtr, int bufferLength); + public static unsafe Task TransformStreamWriteUnsafe(JSObject httpController, ReadOnlyMemory buffer, Buffers.MemoryHandle handle) + => TransformStreamWrite(httpController, (nint)handle.Pointer, buffer.Length); + [JSImport("INTERNAL.http_wasm_transform_stream_close")] public static partial Task TransformStreamClose( - JSObject transformStream); - - [JSImport("INTERNAL.http_wasm_transform_stream_abort")] - public static partial void TransformStreamAbort( - JSObject transformStream); + JSObject httpController); [JSImport("INTERNAL.http_wasm_get_response_header_names")] private static partial string[] _GetResponseHeaderNames( - JSObject fetchResponse); + JSObject httpController); [JSImport("INTERNAL.http_wasm_get_response_header_values")] private static partial string[] _GetResponseHeaderValues( - JSObject fetchResponse); + JSObject httpController); + + [JSImport("INTERNAL.http_wasm_get_response_status")] + public static partial int GetResponseStatus( + JSObject httpController); + + [JSImport("INTERNAL.http_wasm_get_response_type")] + public static partial string GetResponseType( + JSObject httpController); - public static void GetResponseHeaders(JSObject fetchResponse, HttpHeaders resposeHeaders, HttpHeaders contentHeaders) + public static void GetResponseHeaders(JSObject httpController, HttpHeaders resposeHeaders, HttpHeaders contentHeaders) { - string[] headerNames = _GetResponseHeaderNames(fetchResponse); - string[] headerValues = _GetResponseHeaderValues(fetchResponse); + string[] headerNames = _GetResponseHeaderNames(httpController); + string[] headerValues = _GetResponseHeaderValues(httpController); + // Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation + // CORS will only allow access to certain headers on browser. for (int i = 0; i < headerNames.Length; i++) { if (!resposeHeaders.TryAddWithoutValidation(headerNames[i], headerValues[i])) @@ -67,43 +73,38 @@ public static void GetResponseHeaders(JSObject fetchResponse, HttpHeaders respos } } - [JSImport("INTERNAL.http_wasm_fetch")] - public static partial Task Fetch( + public static partial Task Fetch( + JSObject httpController, string uri, string[] headerNames, string[] headerValues, string[] optionNames, - [JSMarshalAs>] object?[] optionValues, - JSObject abortControler); + [JSMarshalAs>] object?[] optionValues); [JSImport("INTERNAL.http_wasm_fetch_stream")] - public static partial Task Fetch( + public static partial Task FetchStream( + JSObject httpController, string uri, string[] headerNames, string[] headerValues, string[] optionNames, - [JSMarshalAs>] object?[] optionValues, - JSObject abortControler, - JSObject transformStream); + [JSMarshalAs>] object?[] optionValues); [JSImport("INTERNAL.http_wasm_fetch_bytes")] - private static partial Task FetchBytes( + private static partial Task FetchBytes( + JSObject httpController, string uri, string[] headerNames, string[] headerValues, string[] optionNames, [JSMarshalAs>] object?[] optionValues, - JSObject abortControler, IntPtr bodyPtr, int bodyLength); - public static unsafe Task Fetch(string uri, string[] headerNames, string[] headerValues, string[] optionNames, object?[] optionValues, JSObject abortControler, byte[] body) + public static unsafe Task FetchBytes(JSObject httpController, string uri, string[] headerNames, string[] headerValues, string[] optionNames, object?[] optionValues, MemoryHandle pinBuffer, int bodyLength) { - fixed (byte* ptr = body) - { - return FetchBytes(uri, headerNames, headerValues, optionNames, optionValues, abortControler, (IntPtr)ptr, body.Length); - } + return FetchBytes(httpController, uri, headerNames, headerValues, optionNames, optionValues, (IntPtr)pinBuffer.Pointer, bodyLength); } [JSImport("INTERNAL.http_wasm_get_streamed_response_bytes")] @@ -112,6 +113,10 @@ public static partial Task GetStreamedResponseBytes( IntPtr bufferPtr, int bufferLength); + public static unsafe Task GetStreamedResponseBytesUnsafe(JSObject jsController, Memory buffer, MemoryHandle handle) + => GetStreamedResponseBytes(jsController, (IntPtr)handle.Pointer, buffer.Length); + + [JSImport("INTERNAL.http_wasm_get_response_length")] public static partial Task GetResponseLength( JSObject fetchResponse); @@ -122,8 +127,10 @@ public static partial int GetResponseBytes( [JSMarshalAs] Span buffer); - public static async ValueTask CancelationHelper(Task promise, CancellationToken cancellationToken, JSObject? fetchResponse = null) + public static async Task CancellationHelper(Task promise, CancellationToken cancellationToken, JSObject jsController) { + Http.CancellationHelper.ThrowIfCancellationRequested(cancellationToken); + if (promise.IsCompletedSuccessfully) { return; @@ -132,46 +139,43 @@ public static async ValueTask CancelationHelper(Task promise, CancellationToken { using (var operationRegistration = cancellationToken.Register(static s => { - (Task _promise, JSObject? _fetchResponse) = ((Task, JSObject?))s!; - CancelablePromise.CancelPromise(_promise, static (JSObject? __fetchResponse) => + (Task _promise, JSObject _jsController) = ((Task, JSObject))s!; + CancelablePromise.CancelPromise(_promise, static (JSObject __jsController) => { - if (__fetchResponse != null) + if (!__jsController.IsDisposed) { - AbortResponse(__fetchResponse); + AbortResponse(__jsController); } - }, _fetchResponse); - }, (promise, fetchResponse))) + }, _jsController); + }, (promise, jsController))) { await promise.ConfigureAwait(true); } } catch (OperationCanceledException oce) when (cancellationToken.IsCancellationRequested) { - throw CancellationHelper.CreateOperationCanceledException(oce, cancellationToken); + Http.CancellationHelper.ThrowIfCancellationRequested(oce, cancellationToken); } catch (JSException jse) { if (jse.Message.StartsWith("AbortError", StringComparison.Ordinal)) { - throw CancellationHelper.CreateOperationCanceledException(jse, CancellationToken.None); - } - if (cancellationToken.IsCancellationRequested) - { - throw CancellationHelper.CreateOperationCanceledException(jse, cancellationToken); + throw Http.CancellationHelper.CreateOperationCanceledException(jse, CancellationToken.None); } + Http.CancellationHelper.ThrowIfCancellationRequested(jse, cancellationToken); throw new HttpRequestException(jse.Message, jse); } } - public static async ValueTask CancelationHelper(Task promise, CancellationToken cancellationToken, JSObject? fetchResponse = null) + public static async Task CancellationHelper(Task promise, CancellationToken cancellationToken, JSObject jsController) { + Http.CancellationHelper.ThrowIfCancellationRequested(cancellationToken); if (promise.IsCompletedSuccessfully) { return promise.Result; } - await CancelationHelper((Task)promise, cancellationToken, fetchResponse).ConfigureAwait(true); - return await promise.ConfigureAwait(true); + await CancellationHelper((Task)promise, cancellationToken, jsController).ConfigureAwait(false); + return promise.Result; } } - } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/CancellationHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/CancellationHelper.cs index e6d18e4a67e0db..cf6fdd6b5f48fe 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/CancellationHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/CancellationHelper.cs @@ -35,10 +35,18 @@ private static void ThrowOperationCanceledException(Exception? innerException, C /// Throws a cancellation exception if cancellation has been requested via . /// The token to check for a cancellation request. internal static void ThrowIfCancellationRequested(CancellationToken cancellationToken) + { + ThrowIfCancellationRequested(innerException: null, cancellationToken); + } + + /// Throws a cancellation exception if cancellation has been requested via . + /// The inner exception to wrap. May be null. + /// The token to check for a cancellation request. + internal static void ThrowIfCancellationRequested(Exception? innerException, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { - ThrowOperationCanceledException(innerException: null, cancellationToken); + ThrowOperationCanceledException(innerException, cancellationToken); } } } 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 773d73a39a0943..2451c867a9489d 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 @@ -16,8 +16,6 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) $(DefineConstants);TargetsWindows $(DefineConstants);TARGETS_BROWSER - - <_XUnitBackgroundExec>false diff --git a/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Add_URL.cs b/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Add_URL.cs index 5bd95c0849eeee..205ad7ebd026ee 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Add_URL.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaSet/TC_SchemaSet_Add_URL.cs @@ -64,7 +64,6 @@ public void v3() //----------------------------------------------------------------------------------- [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/75123", typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] //[Variation(Desc = "v4 - ns = valid, URL = invalid")] public void v4() { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml index 5619a7b22a8f20..93694b543159a5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml @@ -1,11 +1,5 @@  - - CP0001 - T:System.Runtime.InteropServices.JavaScript.SynchronizationContextExtension - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - CP0001 T:System.Runtime.InteropServices.JavaScript.CancelablePromise @@ -18,10 +12,4 @@ ref/net9.0/System.Runtime.InteropServices.JavaScript.dll runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - - CP0002 - M:System.Runtime.InteropServices.JavaScript.JSHost.get_CurrentOrMainJSSynchronizationContext - ref/net9.0/System.Runtime.InteropServices.JavaScript.dll - runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 8ad195d25ad0f5..4a7f303685ba59 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -42,7 +42,6 @@ - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs index b5238826e5bb79..0a685e996882da 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHost.cs @@ -50,17 +50,5 @@ public static Task ImportAsync(string moduleName, string moduleUrl, Ca return JSHostImplementation.ImportAsync(moduleName, moduleUrl, cancellationToken); } - public static SynchronizationContext CurrentOrMainJSSynchronizationContext - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if FEATURE_WASM_THREADS - return (JSProxyContext.ExecutionContext ?? JSProxyContext.MainThreadContext).SynchronizationContext; -#else - return null!; -#endif - } - } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs index cb9016471923e6..a68d7d11969357 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs @@ -32,37 +32,44 @@ public static Task RunAsync(Func body) public static Task RunAsync(Func> body, CancellationToken cancellationToken) { - var instance = new JSWebWorkerInstance(body, null, cancellationToken); + var instance = new JSWebWorkerInstance(body, cancellationToken); return instance.Start(); } public static Task RunAsync(Func body, CancellationToken cancellationToken) { - var instance = new JSWebWorkerInstance(null, body, cancellationToken); + var instance = new JSWebWorkerInstance(async () => + { + await body().ConfigureAwait(false); + return 0; + }, cancellationToken); return instance.Start(); } internal sealed class JSWebWorkerInstance : IDisposable { - private JSSynchronizationContext? _jsSynchronizationContext; - private TaskCompletionSource _taskCompletionSource; - private Thread _thread; - private CancellationToken _cancellationToken; + private readonly TaskCompletionSource _taskCompletionSource; + private readonly Thread _thread; + private readonly CancellationToken _cancellationToken; + private readonly Func> _body; + private CancellationTokenRegistration? _cancellationRegistration; - private Func>? _bodyRes; - private Func? _bodyVoid; - private Task? _resultTask; + private JSSynchronizationContext? _jsSynchronizationContext; + private Task? _resultTask; private bool _isDisposed; - public JSWebWorkerInstance(Func>? bodyRes, Func? bodyVoid, CancellationToken cancellationToken) + public JSWebWorkerInstance(Func> body, CancellationToken cancellationToken) { + // Task created from this TCS is consumed by external caller, on outer thread. + // We don't want the continuations of that task to run on JSWebWorker + // only the tasks created inside of the callback should run in JSWebWorker + // TODO TaskCreationOptions.HideScheduler ? _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _thread = new Thread(ThreadMain); _resultTask = null; _cancellationToken = cancellationToken; _cancellationRegistration = null; - _bodyRes = bodyRes; - _bodyVoid = bodyVoid; + _body = body; JSHostImplementation.SetHasExternalEventLoop(_thread); } @@ -73,14 +80,20 @@ public Task Start() // give browser chance to load more threads // until there at least one thread loaded, it doesn't make sense to `Start` // because that would also hang, but in a way blocking the UI thread, much worse. - JavaScriptImports.ThreadAvailable().ContinueWith(t => + JavaScriptImports.ThreadAvailable().ContinueWith(static (t, o) => { + var self = (JSWebWorkerInstance)o!; if (t.IsCompletedSuccessfully) { - _thread.Start(); + self._thread.Start(); } - return t; - }, _cancellationToken, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Current); + if (t.IsCanceled) + { + throw new OperationCanceledException("Cancelled while waiting for underlying WebWorker to become available.", self._cancellationToken); + } + throw t.Exception!; + // ideally this will execute on UI thread quickly: ExecuteSynchronously + }, this, _cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.FromCurrentSynchronizationContext()); } else { @@ -95,32 +108,26 @@ private void ThreadMain() { if (_cancellationToken.IsCancellationRequested) { - PropagateCompletionAndDispose(Task.FromException(new OperationCanceledException(_cancellationToken))); + PropagateCompletionAndDispose(Task.FromCanceled(_cancellationToken)); return; } // receive callback when the cancellation is requested - _cancellationRegistration = _cancellationToken.Register(() => + _cancellationRegistration = _cancellationToken.Register(static (o) => { + var self = (JSWebWorkerInstance)o!; // this could be executing on any thread - PropagateCompletionAndDispose(Task.FromException(new OperationCanceledException(_cancellationToken))); - }); + self.PropagateCompletionAndDispose(Task.FromCanceled(self._cancellationToken)); + }, this); // JSSynchronizationContext also registers to _cancellationToken _jsSynchronizationContext = JSSynchronizationContext.InstallWebWorkerInterop(false, _cancellationToken); var childScheduler = TaskScheduler.FromCurrentSynchronizationContext(); - if (_bodyRes != null) - { - _resultTask = _bodyRes(); - } - else - { - _resultTask = _bodyVoid!(); - } + // This code is exiting thread ThreadMain() before all promises are resolved. // the continuation is executed by setTimeout() callback of the WebWorker thread. - _resultTask.ContinueWith(PropagateCompletionAndDispose, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, childScheduler); + _body().ContinueWith(PropagateCompletionAndDispose, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, childScheduler); } catch (Exception ex) { @@ -129,7 +136,7 @@ private void ThreadMain() } // run actions on correct thread - private void PropagateCompletionAndDispose(Task result) + private void PropagateCompletionAndDispose(Task result) { _resultTask = result; @@ -170,35 +177,7 @@ private void PropagateCompletionAndDispose(Task result) Dispose(); } - private void PropagateCompletion() - { - if (_resultTask!.IsFaulted) - { - if (_resultTask.Exception is AggregateException ag && ag.InnerException != null) - { - _taskCompletionSource.TrySetException(ag.InnerException); - } - else - { - _taskCompletionSource.TrySetException(_resultTask.Exception); - } - } - else if (_resultTask.IsCanceled) - { - _taskCompletionSource.TrySetCanceled(); - } - else - { - if (_bodyRes != null) - { - _taskCompletionSource.TrySetResult(((Task)_resultTask).Result); - } - else - { - _taskCompletionSource.TrySetResult(default!); - } - } - } + private void PropagateCompletion() => _taskCompletionSource.TrySetFromTask(_resultTask!); private void Dispose(bool disposing) { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index 098dfb18ec2674..d00636cbbfea7d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -49,6 +49,8 @@ public unsafe void ToManaged(out Task? value) lock (ctx) { PromiseHolder holder = ctx.GetPromiseHolder(slot.GCHandle); + // we want to run the continuations on the original thread which called the JSImport, so RunContinuationsAsynchronously, rather than ExecuteSynchronously + // TODO TaskCreationOptions.RunContinuationsAsynchronously TaskCompletionSource tcs = new TaskCompletionSource(holder); ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) => { @@ -98,6 +100,8 @@ public unsafe void ToManaged(out Task? value, ArgumentToManagedCallback lock (ctx) { var holder = ctx.GetPromiseHolder(slot.GCHandle); + // we want to run the continuations on the original thread which called the JSImport, so RunContinuationsAsynchronously, rather than ExecuteSynchronously + // TODO TaskCreationOptions.RunContinuationsAsynchronously TaskCompletionSource tcs = new TaskCompletionSource(holder); ToManagedCallback callback = (JSMarshalerArgument* arguments_buffer) => { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SynchronizationContextExtensions.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SynchronizationContextExtensions.cs deleted file mode 100644 index 6f63db9cfd3375..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SynchronizationContextExtensions.cs +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Threading; -using System.Threading.Tasks; - -namespace System.Runtime.InteropServices.JavaScript -{ - /// - /// Extensions of SynchronizationContext which propagate errors and return values - /// - public static class SynchronizationContextExtension - { - public static void Send(this SynchronizationContext self, Action body, T value) - { - Exception? exc = default; - self.Send((_value) => - { - try - { - body((T)_value!); - } - catch (Exception ex) - { - exc = ex; - } - }, value); - if (exc != null) - { - throw exc; - } - } - - public static TRes Send(this SynchronizationContext self, Func body) - { - TRes? value = default; - Exception? exc = default; - self.Send((_) => - { - try - { - value = body(); - } - catch (Exception ex) - { - exc = ex; - } - }, null); - if (exc != null) - { - throw exc; - } - return value!; - } - - public static Task Post(this SynchronizationContext self, Func> body) - { - TaskCompletionSource tcs = new TaskCompletionSource(); - self.Post(async (_) => - { - try - { - var value = await body().ConfigureAwait(false); - tcs.TrySetResult(value); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, null); - return tcs.Task; - } - - public static Task Post(this SynchronizationContext? self, Func> body, T1 p1) - { - if (self == null) return body(p1); - - TaskCompletionSource tcs = new TaskCompletionSource(); - self.Post(async (_) => - { - try - { - var value = await body(p1).ConfigureAwait(false); - tcs.TrySetResult(value); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, null); - return tcs.Task; - } - - public static Task Post(this SynchronizationContext self, Func body, T1 p1) - { - TaskCompletionSource tcs = new TaskCompletionSource(); - self.Post(async (_) => - { - try - { - await body(p1).ConfigureAwait(false); - tcs.TrySetResult(); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, null); - return tcs.Task; - } - - public static Task Post(this SynchronizationContext self, Func body) - { - TaskCompletionSource tcs = new TaskCompletionSource(); - self.Post(async (_) => - { - try - { - await body().ConfigureAwait(false); - tcs.TrySetResult(); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, null); - return tcs.Task; - } - - public static TRes Send(this SynchronizationContext self, Func body, T1 p1) - { - TRes? value = default; - Exception? exc = default; - self.Send((_) => - { - try - { - value = body(p1); - } - catch (Exception ex) - { - exc = ex; - } - }, null); - if (exc != null) - { - throw exc; - } - return value!; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index a0457f006246c6..90b2454d6e375f 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -12,6 +12,8 @@ $(DefineConstants);FEATURE_WASM_THREADS true + + $(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=true diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs index cd8e24d6343e70..c0c03addd6bb87 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs @@ -28,12 +28,36 @@ namespace System.Runtime.InteropServices.JavaScript.Tests // JS setTimeout till after JSWebWorker close // synchronous .Wait for JS setTimeout on the same thread -> deadlock problem **7)** - public class WebWorkerTest + public class WebWorkerTest : IAsyncLifetime { - const int TimeoutMilliseconds = 300; + const int TimeoutMilliseconds = 5000; + + public static bool _isWarmupDone; + + public async Task InitializeAsync() + { + if (_isWarmupDone) + { + return; + } + await Task.Delay(500); + _isWarmupDone = true; + } + + public Task DisposeAsync() => Task.CompletedTask; #region Executors + private CancellationTokenSource CreateTestCaseTimeoutSource() + { + var cts = new CancellationTokenSource(TimeoutMilliseconds); + cts.Token.Register(() => + { + Console.WriteLine($"Unexpected test case timeout at {DateTime.Now.ToString("u")} ManagedThreadId:{Environment.CurrentManagedThreadId}"); + }); + return cts; + } + public static IEnumerable GetTargetThreads() { return Enum.GetValues().Select(type => new object[] { new Executor(type) }); @@ -55,7 +79,7 @@ public static IEnumerable GetTargetThreads2x() [Theory, MemberData(nameof(GetTargetThreads))] public async Task Executor_Cancellation(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); TaskCompletionSource ready = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var canceledTask = executor.Execute(() => @@ -69,13 +93,13 @@ public async Task Executor_Cancellation(Executor executor) cts.Cancel(); - await Assert.ThrowsAsync(() => canceledTask); + await Assert.ThrowsAnyAsync(() => canceledTask); } [Theory, MemberData(nameof(GetTargetThreads))] public async Task JSDelay_Cancellation(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); TaskCompletionSource ready = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var canceledTask = executor.Execute(async () => { @@ -90,15 +114,15 @@ public async Task JSDelay_Cancellation(Executor executor) cts.Cancel(); - await Assert.ThrowsAsync(() => canceledTask); + await Assert.ThrowsAnyAsync(() => canceledTask); } [Fact] public async Task JSSynchronizationContext_Send_Post_Items_Cancellation() { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); - ManualResetEventSlim blocker=new ManualResetEventSlim(false); + ManualResetEventSlim blocker = new ManualResetEventSlim(false); TaskCompletionSource never = new TaskCompletionSource(); SynchronizationContext capturedSynchronizationContext = null; TaskCompletionSource jswReady = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -157,7 +181,7 @@ public async Task JSSynchronizationContext_Send_Post_Items_Cancellation() // this will unblock the current pending work item blocker.Set(); - await Assert.ThrowsAsync(() => canceledSend); + await Assert.ThrowsAnyAsync(() => canceledSend); await canceledPost; // this shouldn't throw Assert.False(shouldNotHitSend); @@ -168,12 +192,12 @@ public async Task JSSynchronizationContext_Send_Post_Items_Cancellation() [Fact] public async Task JSSynchronizationContext_Send_Post_To_Canceled() { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); TaskCompletionSource never = new TaskCompletionSource(); SynchronizationContext capturedSynchronizationContext = null; TaskCompletionSource jswReady = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - JSObject capturedGlobalThis=null; + JSObject capturedGlobalThis = null; var canceledTask = JSWebWorker.RunAsync(() => { @@ -226,7 +250,7 @@ public async Task JSSynchronizationContext_Send_Post_To_Canceled() [Fact] public async Task JSWebWorker_Abandon_Running() { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); TaskCompletionSource never = new TaskCompletionSource(); TaskCompletionSource ready = new TaskCompletionSource(); @@ -251,7 +275,7 @@ public async Task JSWebWorker_Abandon_Running() [Fact] public async Task JSWebWorker_Abandon_Running_JS() { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); TaskCompletionSource ready = new TaskCompletionSource(); @@ -277,7 +301,7 @@ public async Task JSWebWorker_Abandon_Running_JS() [Theory, MemberData(nameof(GetTargetThreads))] public async Task Executor_Propagates(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); bool hit = false; var failedTask = executor.Execute(() => { @@ -297,7 +321,7 @@ public async Task Executor_Propagates(Executor executor) [Theory, MemberData(nameof(GetTargetThreads))] public async Task ManagedConsole(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(() => { Console.WriteLine("C# Hello from ManagedThreadId: " + Environment.CurrentManagedThreadId); @@ -308,7 +332,7 @@ await executor.Execute(() => [Theory, MemberData(nameof(GetTargetThreads))] public async Task JSConsole(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(() => { WebWorkerTestHelper.Log("JS Hello from ManagedThreadId: " + Environment.CurrentManagedThreadId + " NativeThreadId: " + WebWorkerTestHelper.NativeThreadId); @@ -319,7 +343,7 @@ await executor.Execute(() => [Theory, MemberData(nameof(GetTargetThreads))] public async Task NativeThreadId(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { await executor.StickyAwait(WebWorkerTestHelper.InitializeAsync(), cts.Token); @@ -343,7 +367,7 @@ await executor.Execute(async () => public async Task ThreadingTimer(Executor executor) { var hit = false; - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { TaskCompletionSource tcs = new TaskCompletionSource(); @@ -365,7 +389,7 @@ await executor.Execute(async () => [Theory, MemberData(nameof(GetTargetThreads))] public async Task JSDelay_ContinueWith(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { await executor.StickyAwait(WebWorkerTestHelper.CreateDelay(), cts.Token); @@ -381,7 +405,7 @@ await WebWorkerTestHelper.JSDelay(10).ContinueWith(_ => [Theory, MemberData(nameof(GetTargetThreads))] public async Task JSDelay_ConfigureAwait_True(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { await executor.StickyAwait(WebWorkerTestHelper.CreateDelay(), cts.Token); @@ -396,7 +420,7 @@ await executor.Execute(async () => public async Task ManagedDelay_ContinueWith(Executor executor) { var hit = false; - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { await Task.Delay(10, cts.Token).ContinueWith(_ => @@ -410,7 +434,7 @@ await Task.Delay(10, cts.Token).ContinueWith(_ => [Theory, MemberData(nameof(GetTargetThreads))] public async Task ManagedDelay_ConfigureAwait_True(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { await Task.Delay(10, cts.Token).ConfigureAwait(true); @@ -422,7 +446,7 @@ await executor.Execute(async () => [Theory, MemberData(nameof(GetTargetThreads))] public async Task ManagedYield(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { await Task.Yield(); @@ -474,7 +498,7 @@ private async Task ActionsInDifferentThreads(Executor executor1, Executor exe [Theory, MemberData(nameof(GetTargetThreads2x))] public async Task JSObject_CapturesAffinity(Executor executor1, Executor executor2) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); var e1Job = async (Task e2done, TaskCompletionSource e1State) => { @@ -509,7 +533,7 @@ public async Task JSObject_CapturesAffinity(Executor executor1, Executor executo [Theory, MemberData(nameof(GetTargetThreads))] public async Task WebSocketClient_ContentInSameThread(Executor executor) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); var uri = new Uri(WebWorkerTestHelper.LocalWsEcho + "?guid=" + Guid.NewGuid()); var message = "hello"; @@ -534,7 +558,7 @@ await executor.Execute(async () => [Theory, MemberData(nameof(GetTargetThreads2x))] public Task WebSocketClient_ResponseCloseInDifferentThread(Executor executor1, Executor executor2) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = CreateTestCaseTimeoutSource(); var uri = new Uri(WebWorkerTestHelper.LocalWsEcho + "?guid=" + Guid.NewGuid()); var message = "hello"; @@ -569,7 +593,7 @@ public Task WebSocketClient_ResponseCloseInDifferentThread(Executor executor1, E [Theory, MemberData(nameof(GetTargetThreads2x))] public Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor executor2) { - var cts = new CancellationTokenSource(TimeoutMilliseconds); + var cts = new CancellationTokenSource(); var uri = new Uri(WebWorkerTestHelper.LocalWsEcho + "?guid=" + Guid.NewGuid()); var message = ".delay5sec"; // this will make the loopback server slower @@ -592,7 +616,7 @@ public Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor CancellationTokenSource cts2 = new CancellationTokenSource(); var resTask = client.ReceiveAsync(receive, cts2.Token); cts2.Cancel(); - var ex = await Assert.ThrowsAsync(() => resTask); + var ex = await Assert.ThrowsAnyAsync(() => resTask); Assert.Equal(cts2.Token, ex.CancellationToken); }; @@ -600,5 +624,82 @@ public Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor } #endregion + + #region HTTP + + [Theory, MemberData(nameof(GetTargetThreads))] + public async Task HttpClient_ContentInSameThread(Executor executor) + { + var cts = CreateTestCaseTimeoutSource(); + var uri = WebWorkerTestHelper.GetOriginUrl() + "/_framework/blazor.boot.json"; + + await executor.Execute(async () => + { + using var client = new HttpClient(); + using var response = await client.GetAsync(uri); + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Assert.StartsWith("{", body); + }, cts.Token); + } + + private static HttpRequestOptionsKey WebAssemblyEnableStreamingRequestKey = new("WebAssemblyEnableStreamingRequest"); + private static HttpRequestOptionsKey WebAssemblyEnableStreamingResponseKey = new("WebAssemblyEnableStreamingResponse"); + private static string HelloJson = "{'hello':'world'}".Replace('\'', '"'); + private static string EchoStart = "{\"Method\":\"POST\",\"Url\":\"/Echo.ashx"; + + private Task HttpClient_ActionInDifferentThread(string url, Executor executor1, Executor executor2, Func e2Job) + { + var cts = CreateTestCaseTimeoutSource(); + + var e1Job = async (Task e2done, TaskCompletionSource e1State) => + { + using var ms = new MemoryStream(); + await ms.WriteAsync(Encoding.UTF8.GetBytes(HelloJson)); + + using var req = new HttpRequestMessage(HttpMethod.Post, url); + req.Options.Set(WebAssemblyEnableStreamingResponseKey, true); + req.Content = new StreamContent(ms); + using var client = new HttpClient(); + var pr = client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead); + using var response = await pr; + + // share the state with the E2 continuation + e1State.SetResult(response); + + await e2done; + }; + return ActionsInDifferentThreads(executor1, executor2, e1Job, e2Job, cts); + } + + [Theory, MemberData(nameof(GetTargetThreads2x))] + public async Task HttpClient_ContentInDifferentThread(Executor executor1, Executor executor2) + { + var url = WebWorkerTestHelper.LocalHttpEcho + "?guid=" + Guid.NewGuid(); + await HttpClient_ActionInDifferentThread(url, executor1, executor2, async (HttpResponseMessage response) => + { + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Assert.StartsWith(EchoStart, body); + }); + } + + [Theory, MemberData(nameof(GetTargetThreads2x))] + public async Task HttpClient_CancelInDifferentThread(Executor executor1, Executor executor2) + { + var url = WebWorkerTestHelper.LocalHttpEcho + "?delay10sec=true&guid=" + Guid.NewGuid(); + await HttpClient_ActionInDifferentThread(url, executor1, executor2, async (HttpResponseMessage response) => + { + await Assert.ThrowsAsync(async () => + { + CancellationTokenSource cts = new CancellationTokenSource(); + var promise = response.Content.ReadAsStringAsync(cts.Token); + cts.Cancel(); + await promise; + }); + }); + } + + #endregion } } diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 043b553b00ab90..2595b93e91b4b8 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -604,9 +604,9 @@ + diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts index 3670e7c42389d3..09a1375726152e 100644 --- a/src/mono/browser/runtime/dotnet.d.ts +++ b/src/mono/browser/runtime/dotnet.d.ts @@ -336,7 +336,11 @@ type AssetBehaviors = SingleAssetBehaviors | /** * The javascript module for threads. */ - | "symbols"; + | "symbols" +/** + * Load segmentation rules file for Hybrid Globalization. + */ + | "segmentation-rules"; declare const enum GlobalizationMode { /** * Load sharded ICU data. diff --git a/src/mono/browser/runtime/exports-internal.ts b/src/mono/browser/runtime/exports-internal.ts index f81a7b95a5b07e..d174432d1209e8 100644 --- a/src/mono/browser/runtime/exports-internal.ts +++ b/src/mono/browser/runtime/exports-internal.ts @@ -4,7 +4,7 @@ import { mono_wasm_cancel_promise } from "./cancelable-promise"; import cwraps, { profiler_c_functions } from "./cwraps"; import { mono_wasm_send_dbg_command_with_parms, mono_wasm_send_dbg_command, mono_wasm_get_dbg_command_info, mono_wasm_get_details, mono_wasm_release_object, mono_wasm_call_function_on, mono_wasm_debugger_resume, mono_wasm_detach_debugger, mono_wasm_raise_debug_event, mono_wasm_change_debugger_log_level, mono_wasm_debugger_attached } from "./debug"; -import { http_wasm_supports_streaming_request, http_wasm_supports_streaming_response, http_wasm_create_abort_controler, http_wasm_abort_request, http_wasm_abort_response, http_wasm_create_transform_stream, http_wasm_transform_stream_write, http_wasm_transform_stream_close, http_wasm_transform_stream_abort, http_wasm_fetch, http_wasm_fetch_stream, http_wasm_fetch_bytes, http_wasm_get_response_header_names, http_wasm_get_response_header_values, http_wasm_get_response_bytes, http_wasm_get_response_length, http_wasm_get_streamed_response_bytes } from "./http"; +import { http_wasm_supports_streaming_request, http_wasm_supports_streaming_response, http_wasm_create_controller, http_wasm_abort_request, http_wasm_abort_response, http_wasm_transform_stream_write, http_wasm_transform_stream_close, http_wasm_fetch, http_wasm_fetch_stream, http_wasm_fetch_bytes, http_wasm_get_response_header_names, http_wasm_get_response_header_values, http_wasm_get_response_bytes, http_wasm_get_response_length, http_wasm_get_streamed_response_bytes, http_wasm_get_response_type, http_wasm_get_response_status } from "./http"; import { exportedRuntimeAPI, Module, runtimeHelpers } from "./globals"; import { get_property, set_property, has_property, get_typeof_property, get_global_this, dynamic_import } from "./invoke-js"; import { mono_wasm_stringify_as_error_with_stack } from "./logging"; @@ -71,13 +71,13 @@ export function export_internal(): any { // BrowserHttpHandler http_wasm_supports_streaming_request, http_wasm_supports_streaming_response, - http_wasm_create_abort_controler, + http_wasm_create_controller, + http_wasm_get_response_type, + http_wasm_get_response_status, http_wasm_abort_request, http_wasm_abort_response, - http_wasm_create_transform_stream, http_wasm_transform_stream_write, http_wasm_transform_stream_close, - http_wasm_transform_stream_abort, http_wasm_fetch, http_wasm_fetch_stream, http_wasm_fetch_bytes, diff --git a/src/mono/browser/runtime/http.ts b/src/mono/browser/runtime/http.ts index 64026166f1a593..bf29d8e0eaa142 100644 --- a/src/mono/browser/runtime/http.ts +++ b/src/mono/browser/runtime/http.ts @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import BuildConfiguration from "consts:configuration"; + import { wrap_as_cancelable_promise } from "./cancelable-promise"; import { ENVIRONMENT_IS_NODE, Module, loaderHelpers, mono_assert } from "./globals"; import { assert_js_interop } from "./invoke-js"; @@ -18,6 +20,11 @@ function verifyEnvironment() { } } +function commonAsserts(controller: HttpController) { + assert_js_interop(); + mono_assert(controller, "expected controller"); +} + export function http_wasm_supports_streaming_request(): boolean { // Detecting streaming request support works like this: // If the browser doesn't support a particular body type, it calls toString() on the object and uses the result as the body. @@ -45,19 +52,27 @@ export function http_wasm_supports_streaming_response(): boolean { return typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function"; } -export function http_wasm_create_abort_controler(): AbortController { +export function http_wasm_create_controller(): HttpController { verifyEnvironment(); - return new AbortController(); + assert_js_interop(); + const controller: HttpController = { + abortController: new AbortController() + }; + return controller; } -export function http_wasm_abort_request(abort_controller: AbortController): void { - abort_controller.abort(); +export function http_wasm_abort_request(controller: HttpController): void { + if (controller.streamWriter) { + controller.streamWriter.abort(); + } + http_wasm_abort_response(controller); } -export function http_wasm_abort_response(res: ResponseExtension): void { - res.__abort_controller.abort(); - if (res.__reader) { - res.__reader.cancel().catch((err) => { +export function http_wasm_abort_response(controller: HttpController): void { + if (BuildConfiguration === "Debug") commonAsserts(controller); + controller.abortController.abort(); + if (controller.streamReader) { + controller.streamReader.cancel().catch((err) => { if (err && err.name !== "AbortError") { Module.err("Error in http_wasm_abort_response: " + err); } @@ -66,57 +81,56 @@ export function http_wasm_abort_response(res: ResponseExtension): void { } } -export function http_wasm_create_transform_stream(): TransformStreamExtension { - const transform_stream = new TransformStream() as TransformStreamExtension; - transform_stream.__writer = transform_stream.writable.getWriter(); - return transform_stream; -} - -export function http_wasm_transform_stream_write(ts: TransformStreamExtension, bufferPtr: VoidPtr, bufferLength: number): ControllablePromise { +export function http_wasm_transform_stream_write(controller: HttpController, bufferPtr: VoidPtr, bufferLength: number): ControllablePromise { + if (BuildConfiguration === "Debug") commonAsserts(controller); mono_assert(bufferLength > 0, "expected bufferLength > 0"); // the bufferPtr is pinned by the caller const view = new Span(bufferPtr, bufferLength, MemoryViewType.Byte); const copy = view.slice() as Uint8Array; return wrap_as_cancelable_promise(async () => { - mono_assert(ts.__fetch_promise, "expected fetch promise"); + mono_assert(controller.streamWriter, "expected streamWriter"); + mono_assert(controller.responsePromise, "expected fetch promise"); // race with fetch because fetch does not cancel the ReadableStream see https://bugs.chromium.org/p/chromium/issues/detail?id=1480250 - await Promise.race([ts.__writer.ready, ts.__fetch_promise]); - await Promise.race([ts.__writer.write(copy), ts.__fetch_promise]); + await Promise.race([controller.streamWriter.ready, controller.responsePromise]); + await Promise.race([controller.streamWriter.write(copy), controller.responsePromise]); }); } -export function http_wasm_transform_stream_close(ts: TransformStreamExtension): ControllablePromise { +export function http_wasm_transform_stream_close(controller: HttpController): ControllablePromise { + mono_assert(controller, "expected controller"); return wrap_as_cancelable_promise(async () => { - mono_assert(ts.__fetch_promise, "expected fetch promise"); + mono_assert(controller.streamWriter, "expected streamWriter"); + mono_assert(controller.responsePromise, "expected fetch promise"); // race with fetch because fetch does not cancel the ReadableStream see https://bugs.chromium.org/p/chromium/issues/detail?id=1480250 - await Promise.race([ts.__writer.ready, ts.__fetch_promise]); - await Promise.race([ts.__writer.close(), ts.__fetch_promise]); + await Promise.race([controller.streamWriter.ready, controller.responsePromise]); + await Promise.race([controller.streamWriter.close(), controller.responsePromise]); }); } -export function http_wasm_transform_stream_abort(ts: TransformStreamExtension): void { - ts.__writer.abort(); -} - -export function http_wasm_fetch_stream(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: TransformStreamExtension): ControllablePromise { - const fetch_promise = http_wasm_fetch(url, header_names, header_values, option_names, option_values, abort_controller, body.readable); - body.__fetch_promise = fetch_promise; +export function http_wasm_fetch_stream(controller: HttpController, url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[]): ControllablePromise { + if (BuildConfiguration === "Debug") commonAsserts(controller); + const transformStream = new TransformStream(); + controller.streamWriter = transformStream.writable.getWriter(); + const fetch_promise = http_wasm_fetch(controller, url, header_names, header_values, option_names, option_values, transformStream.readable); return fetch_promise; } -export function http_wasm_fetch_bytes(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, bodyPtr: VoidPtr, bodyLength: number): ControllablePromise { +export function http_wasm_fetch_bytes(controller: HttpController, url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], bodyPtr: VoidPtr, bodyLength: number): ControllablePromise { + if (BuildConfiguration === "Debug") commonAsserts(controller); // the bodyPtr is pinned by the caller const view = new Span(bodyPtr, bodyLength, MemoryViewType.Byte); const copy = view.slice() as Uint8Array; - return http_wasm_fetch(url, header_names, header_values, option_names, option_values, abort_controller, copy); + return http_wasm_fetch(controller, url, header_names, header_values, option_names, option_values, copy); } -export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: Uint8Array | ReadableStream | null): ControllablePromise { +export function http_wasm_fetch(controller: HttpController, url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], body: Uint8Array | ReadableStream | null): ControllablePromise { + if (BuildConfiguration === "Debug") commonAsserts(controller); verifyEnvironment(); assert_js_interop(); mono_assert(url && typeof url === "string", "expected url string"); mono_assert(header_names && header_values && Array.isArray(header_names) && Array.isArray(header_values) && header_names.length === header_values.length, "expected headerNames and headerValues arrays"); mono_assert(option_names && option_values && Array.isArray(option_names) && Array.isArray(option_values) && option_names.length === option_values.length, "expected headerNames and headerValues arrays"); + const headers = new Headers(); for (let i = 0; i < header_names.length; i++) { headers.append(header_names[i], header_values[i]); @@ -124,7 +138,7 @@ export function http_wasm_fetch(url: string, header_names: string[], header_valu const options: any = { body, headers, - signal: abort_controller.signal + signal: controller.abortController.signal }; if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) { options.duplex = "half"; @@ -132,101 +146,125 @@ export function http_wasm_fetch(url: string, header_names: string[], header_valu for (let i = 0; i < option_names.length; i++) { options[option_names[i]] = option_values[i]; } - - return wrap_as_cancelable_promise(async () => { - const res = await loaderHelpers.fetch_like(url, options) as ResponseExtension; - res.__abort_controller = abort_controller; - return res; + // make the fetch cancellable + controller.responsePromise = wrap_as_cancelable_promise(() => { + return loaderHelpers.fetch_like(url, options); }); -} - -function get_response_headers(res: ResponseExtension): void { - if (!res.__headerNames) { - res.__headerNames = []; - res.__headerValues = []; + // avoid processing headers if the fetch is cancelled + controller.responsePromise.then((res: Response) => { + controller.response = res; + controller.responseHeaderNames = []; + controller.responseHeaderValues = []; if (res.headers && (res.headers).entries) { const entries: Iterable = (res.headers).entries(); for (const pair of entries) { - res.__headerNames.push(pair[0]); - res.__headerValues.push(pair[1]); + controller.responseHeaderNames.push(pair[0]); + controller.responseHeaderValues.push(pair[1]); } } - } + }).catch(() => { + // ignore + }); + return controller.responsePromise; } -export function http_wasm_get_response_header_names(res: ResponseExtension): string[] { - get_response_headers(res); - return res.__headerNames; +export function http_wasm_get_response_type(controller: HttpController): string | undefined { + if (BuildConfiguration === "Debug") commonAsserts(controller); + return controller.response?.type; } -export function http_wasm_get_response_header_values(res: ResponseExtension): string[] { - get_response_headers(res); - return res.__headerValues; +export function http_wasm_get_response_status(controller: HttpController): number { + if (BuildConfiguration === "Debug") commonAsserts(controller); + return controller.response?.status ?? 0; +} + + +export function http_wasm_get_response_header_names(controller: HttpController): string[] { + if (BuildConfiguration === "Debug") commonAsserts(controller); + mono_assert(controller.responseHeaderNames, "expected responseHeaderNames"); + return controller.responseHeaderNames; } -export function http_wasm_get_response_length(res: ResponseExtension): ControllablePromise { +export function http_wasm_get_response_header_values(controller: HttpController): string[] { + if (BuildConfiguration === "Debug") commonAsserts(controller); + mono_assert(controller.responseHeaderValues, "expected responseHeaderValues"); + return controller.responseHeaderValues; +} + +export function http_wasm_get_response_length(controller: HttpController): ControllablePromise { + if (BuildConfiguration === "Debug") commonAsserts(controller); return wrap_as_cancelable_promise(async () => { - const buffer = await res.arrayBuffer(); - res.__buffer = buffer; - res.__source_offset = 0; + const buffer = await controller.response!.arrayBuffer(); + controller.responseBuffer = buffer; + controller.currentBufferOffset = 0; return buffer.byteLength; }); } -export function http_wasm_get_response_bytes(res: ResponseExtension, view: Span): number { - mono_assert(res.__buffer, "expected resoved arrayBuffer"); - if (res.__source_offset == res.__buffer!.byteLength) { +export function http_wasm_get_response_bytes(controller: HttpController, view: Span): number { + mono_assert(controller, "expected controller"); + mono_assert(controller.responseBuffer, "expected resoved arrayBuffer"); + mono_assert(controller.currentBufferOffset != undefined, "expected currentBufferOffset"); + if (controller.currentBufferOffset == controller.responseBuffer!.byteLength) { return 0; } - const source_view = new Uint8Array(res.__buffer!, res.__source_offset); + const source_view = new Uint8Array(controller.responseBuffer!, controller.currentBufferOffset); view.set(source_view, 0); const bytes_read = Math.min(view.byteLength, source_view.byteLength); - res.__source_offset += bytes_read; + controller.currentBufferOffset += bytes_read; return bytes_read; } -export function http_wasm_get_streamed_response_bytes(res: ResponseExtension, bufferPtr: VoidPtr, bufferLength: number): ControllablePromise { +export function http_wasm_get_streamed_response_bytes(controller: HttpController, bufferPtr: VoidPtr, bufferLength: number): ControllablePromise { + if (BuildConfiguration === "Debug") commonAsserts(controller); // the bufferPtr is pinned by the caller const view = new Span(bufferPtr, bufferLength, MemoryViewType.Byte); return wrap_as_cancelable_promise(async () => { - if (!res.__reader) { - res.__reader = res.body!.getReader(); + mono_assert(controller.response, "expected response"); + if (!controller.streamReader) { + controller.streamReader = controller.response.body!.getReader(); } - if (!res.__chunk) { - res.__chunk = await res.__reader.read(); - res.__source_offset = 0; + if (!controller.currentStreamReaderChunk || controller.currentBufferOffset === undefined) { + controller.currentStreamReaderChunk = await controller.streamReader.read(); + controller.currentBufferOffset = 0; } - if (res.__chunk.done) { + if (controller.currentStreamReaderChunk.done) { return 0; } - const remaining_source = res.__chunk.value.byteLength - res.__source_offset; + const remaining_source = controller.currentStreamReaderChunk.value.byteLength - controller.currentBufferOffset; mono_assert(remaining_source > 0, "expected remaining_source to be greater than 0"); const bytes_copied = Math.min(remaining_source, view.byteLength); - const source_view = res.__chunk.value.subarray(res.__source_offset, res.__source_offset + bytes_copied); + const source_view = controller.currentStreamReaderChunk.value.subarray(controller.currentBufferOffset, controller.currentBufferOffset + bytes_copied); view.set(source_view, 0); - res.__source_offset += bytes_copied; + controller.currentBufferOffset += bytes_copied; if (remaining_source == bytes_copied) { - res.__chunk = undefined; + controller.currentStreamReaderChunk = undefined; } return bytes_copied; }); } -interface TransformStreamExtension extends TransformStream { - __writer: WritableStreamDefaultWriter - __fetch_promise?: Promise -} +interface HttpController { + abortController: AbortController + + // streaming request + streamReader?: ReadableStreamDefaultReader + + // response + responsePromise?: ControllablePromise + response?: Response + responseHeaderNames?: string[]; + responseHeaderValues?: string[]; + currentBufferOffset?: number + + // non-streaming response + responseBuffer?: ArrayBuffer -interface ResponseExtension extends Response { - __buffer?: ArrayBuffer - __reader?: ReadableStreamDefaultReader - __chunk?: ReadableStreamReadResult - __source_offset: number - __abort_controller: AbortController - __headerNames: string[]; - __headerValues: string[]; + // streaming response + streamWriter?: WritableStreamDefaultWriter + currentStreamReaderChunk?: ReadableStreamReadResult } diff --git a/src/mono/browser/runtime/loader/logging.ts b/src/mono/browser/runtime/loader/logging.ts index 108c354e23a668..e8fba663b78109 100644 --- a/src/mono/browser/runtime/loader/logging.ts +++ b/src/mono/browser/runtime/loader/logging.ts @@ -98,13 +98,13 @@ export function setup_proxy_console(id: string, console: Console, origin: string ...console }; - setupWS(); - const consoleUrl = `${origin}/console`.replace("https://", "wss://").replace("http://", "ws://"); consoleWebSocket = new WebSocket(consoleUrl); consoleWebSocket.addEventListener("error", logWSError); consoleWebSocket.addEventListener("close", logWSClose); + + setupWS(); } export function teardown_proxy_console(message?: string) { @@ -135,7 +135,7 @@ export function teardown_proxy_console(message?: string) { } function send(msg: string) { - if (consoleWebSocket.readyState === WebSocket.OPEN) { + if (consoleWebSocket && consoleWebSocket.readyState === WebSocket.OPEN) { consoleWebSocket.send(msg); } else { From 6fafbad67235cc589ff3e1928e764e22e7bd1d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sun, 21 Jan 2024 21:11:34 +0900 Subject: [PATCH 152/189] Get rid of `EETypePtr` (#97207) Resolves dotnet/runtimelab#232. This wrapper served two purposes over `MethodTable*`: * Make sure equality semantics were enforced at the time where we could have multiple `MethodTable*` representing the same type. This is no longer the case and we don't need it for this purpose. * Save us from typing `unsafe` Apart from this, it also pessimized codegen (see the linked issue). Bye `EETypePtr`. I didn't fully delete it because the `CorElementType` conversion is still there. We can deal with that later. --- src/coreclr/jit/importercalls.cpp | 9 - src/coreclr/jit/namedintrinsiclist.h | 1 - .../System/Runtime/CachedInterfaceDispatch.cs | 16 +- .../src/System/Runtime/EETypePtr.cs | 48 --- .../src/System/Runtime/ExceptionHandling.cs | 4 +- .../src/System/Runtime/InternalCalls.cs | 4 +- .../src/System/Runtime/RuntimeExports.cs | 19 +- .../src/System/Runtime/RuntimeImports.cs | 2 +- .../IntrinsicSupport/ComparerHelpers.cs | 24 +- .../EqualityComparerHelpers.cs | 25 +- .../Reflection/Augments/ReflectionAugments.cs | 12 +- .../Runtime/Augments/RuntimeAugments.cs | 169 ++++---- .../Runtime/CompilerHelpers/ArrayHelpers.cs | 32 +- .../Runtime/CompilerHelpers/InteropHelpers.cs | 22 +- .../Runtime/CompilerHelpers/LdTokenHelpers.cs | 4 +- .../CompilerServices/OpenMethodResolver.cs | 34 +- .../IDynamicInterfaceCastableSupport.cs | 2 +- .../src/Internal/Runtime/ThreadStatics.cs | 2 +- .../src/System/Activator.NativeAot.cs | 3 +- .../src/System/Array.NativeAot.cs | 136 +++---- .../src/System/Delegate.cs | 19 +- .../src/System/EETypePtr.cs | 375 +----------------- .../src/System/Enum.NativeAot.cs | 33 +- .../src/System/InvokeUtils.cs | 72 ++-- .../src/System/MulticastDelegate.cs | 4 +- .../src/System/Object.NativeAot.cs | 10 +- .../System/Reflection/DynamicInvokeInfo.cs | 90 +++-- .../RuntimeHelpers.NativeAot.cs | 28 +- .../InteropServices/ComWrappers.NativeAot.cs | 4 +- .../InteropServices/InteropExtensions.cs | 31 +- .../InteropServices/Marshal.NativeAot.cs | 12 +- .../ObjectiveCMarshal.NativeAot.cs | 2 +- .../Runtime/InteropServices/PInvokeMarshal.cs | 7 +- .../src/System/Runtime/RuntimeImports.cs | 41 +- .../src/System/Runtime/TypeLoaderExports.cs | 3 +- .../src/System/RuntimeType.cs | 6 +- .../src/System/RuntimeTypeHandle.cs | 13 +- .../src/System/String.NativeAot.cs | 6 +- .../src/System/TypedReference.cs | 11 +- .../src/System/ValueType.cs | 10 +- .../src/System/Runtime/RuntimeImports.cs | 6 - .../Test.CoreLib/src/Test.CoreLib.csproj | 3 - .../IL/ILImporter.Scanner.cs | 5 +- .../JitInterface/CorInfoImpl.RyuJit.cs | 1 - 44 files changed, 437 insertions(+), 923 deletions(-) delete mode 100644 src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index ff087f04e4b0e9..1c0ab439147236 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2788,7 +2788,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_Internal_Runtime_MethodTable_Of: case NI_System_Activator_AllocatorOf: case NI_System_Activator_DefaultConstructorOf: - case NI_System_EETypePtr_EETypePtrOf: betterToExpand = true; break; @@ -2981,7 +2980,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_Internal_Runtime_MethodTable_Of: case NI_System_Activator_AllocatorOf: case NI_System_Activator_DefaultConstructorOf: - case NI_System_EETypePtr_EETypePtrOf: { assert(IsTargetAbi(CORINFO_NATIVEAOT_ABI)); // Only NativeAOT supports it. CORINFO_RESOLVED_TOKEN resolvedToken; @@ -8830,13 +8828,6 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_System_Enum_HasFlag; } } - else if (strcmp(className, "EETypePtr") == 0) - { - if (strcmp(methodName, "EETypePtrOf") == 0) - { - result = NI_System_EETypePtr_EETypePtrOf; - } - } break; } diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 2e68fd501243ce..9c4f44e8e2f0bf 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -101,7 +101,6 @@ enum NamedIntrinsic : unsigned short NI_System_Activator_AllocatorOf, NI_System_Activator_DefaultConstructorOf, - NI_System_EETypePtr_EETypePtrOf, NI_Internal_Runtime_MethodTable_Of, diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs index ee9c669b6cb35e..afb4c9a509f4f6 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs @@ -73,7 +73,7 @@ private static IntPtr RhpResolveInterfaceMethod(object pObject, IntPtr pCell) } [RuntimeExport("RhResolveDispatch")] - private static IntPtr RhResolveDispatch(object pObject, EETypePtr interfaceType, ushort slot) + private static IntPtr RhResolveDispatch(object pObject, MethodTable* interfaceType, ushort slot) { DispatchCellInfo cellInfo = default; cellInfo.CellType = DispatchCellType.InterfaceAndSlot; @@ -84,18 +84,12 @@ private static IntPtr RhResolveDispatch(object pObject, EETypePtr interfaceType, } [RuntimeExport("RhResolveDispatchOnType")] - private static IntPtr RhResolveDispatchOnType(EETypePtr instanceType, EETypePtr interfaceType, ushort slot, EETypePtr* pGenericContext) + private static IntPtr RhResolveDispatchOnType(MethodTable* pInstanceType, MethodTable* pInterfaceType, ushort slot, MethodTable** ppGenericContext) { - // Type of object we're dispatching on. - MethodTable* pInstanceType = instanceType.ToPointer(); - - // Type of interface - MethodTable* pInterfaceType = interfaceType.ToPointer(); - return DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType, pInterfaceType, slot, - (MethodTable**)pGenericContext); + ppGenericContext); } private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell, ref DispatchCellInfo cellInfo) @@ -110,7 +104,7 @@ private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell, MethodTable* pResolvingInstanceType = pInstanceType; IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, - cellInfo.InterfaceType.ToPointer(), + cellInfo.InterfaceType, cellInfo.InterfaceSlot, ppGenericContext: null); if (pTargetCode == IntPtr.Zero && pInstanceType->IsIDynamicInterfaceCastable) @@ -119,7 +113,7 @@ private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell, // This will either give us the appropriate result, or throw. var pfnGetInterfaceImplementation = (delegate*) pInstanceType->GetClasslibFunction(ClassLibFunctionId.IDynamicCastableGetInterfaceImplementation); - pTargetCode = pfnGetInterfaceImplementation(pObject, cellInfo.InterfaceType.ToPointer(), cellInfo.InterfaceSlot); + pTargetCode = pfnGetInterfaceImplementation(pObject, cellInfo.InterfaceType, cellInfo.InterfaceSlot); Diagnostics.Debug.Assert(pTargetCode != IntPtr.Zero); } return pTargetCode; diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs deleted file mode 100644 index 8dc88893257430..00000000000000 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/EETypePtr.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -/*============================================================ -** -** Class: EETypePtr -** -** -** Purpose: Pointer Type to a MethodTable in the runtime. -** -** -===========================================================*/ - -namespace System -{ - // This type does not implement GetHashCode but implements Equals -#pragma warning disable 0659 - - [StructLayout(LayoutKind.Sequential)] - public struct EETypePtr - { - private IntPtr _value; - - internal EETypePtr(IntPtr value) - { - _value = value; - } - - internal bool Equals(EETypePtr p) - { - return (_value == p._value); - } - - internal unsafe Internal.Runtime.MethodTable* ToPointer() - { - return (Internal.Runtime.MethodTable*)(void*)_value; - } - - [Intrinsic] - internal static EETypePtr EETypePtrOf() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs index fac623a25cbaff..c29e68d1c050a8 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -386,14 +386,14 @@ public static void ThrowClasslibDivideByZeroException(IntPtr address) } [RuntimeExport("RhExceptionHandling_FailedAllocation")] - public static void FailedAllocation(EETypePtr pEEType, bool fIsOverflow) + public static void FailedAllocation(MethodTable* pEEType, bool fIsOverflow) { ExceptionIDs exID = fIsOverflow ? ExceptionIDs.Overflow : ExceptionIDs.OutOfMemory; // Throw the out of memory exception defined by the classlib, using the input MethodTable* // to find the correct classlib. - throw pEEType.ToPointer()->GetClasslibException(exID); + throw pEEType->GetClasslibException(exID); } #if !INPLACE_RUNTIME diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs index 1abbe8b16f7955..806208187e92fc 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs @@ -21,10 +21,10 @@ internal enum DispatchCellType VTableOffset = 0x2, } - internal struct DispatchCellInfo + internal unsafe struct DispatchCellInfo { public DispatchCellType CellType; - public EETypePtr InterfaceType; + public MethodTable* InterfaceType; public ushort InterfaceSlot; public byte HasCache; public uint MetadataToken; diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs index abeb0c09b46abf..8c83e3cc3c836f 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -164,20 +164,19 @@ private static unsafe bool UnboxAnyTypeCompare(MethodTable* pEEType, MethodTable } [RuntimeExport("RhUnboxAny")] - public static unsafe void RhUnboxAny(object? o, ref byte data, EETypePtr pUnboxToEEType) + public static unsafe void RhUnboxAny(object? o, ref byte data, MethodTable* pUnboxToEEType) { - MethodTable* ptrUnboxToEEType = (MethodTable*)pUnboxToEEType.ToPointer(); - if (ptrUnboxToEEType->IsValueType) + if (pUnboxToEEType->IsValueType) { bool isValid = false; - if (ptrUnboxToEEType->IsNullable) + if (pUnboxToEEType->IsNullable) { - isValid = (o == null) || o.GetMethodTable() == ptrUnboxToEEType->NullableType; + isValid = (o == null) || o.GetMethodTable() == pUnboxToEEType->NullableType; } else { - isValid = (o != null) && UnboxAnyTypeCompare(o.GetMethodTable(), ptrUnboxToEEType); + isValid = (o != null) && UnboxAnyTypeCompare(o.GetMethodTable(), pUnboxToEEType); } if (!isValid) @@ -187,16 +186,16 @@ public static unsafe void RhUnboxAny(object? o, ref byte data, EETypePtr pUnboxT ExceptionIDs exID = o == null ? ExceptionIDs.NullReference : ExceptionIDs.InvalidCast; - throw ptrUnboxToEEType->GetClasslibException(exID); + throw pUnboxToEEType->GetClasslibException(exID); } - RhUnbox(o, ref data, ptrUnboxToEEType); + RhUnbox(o, ref data, pUnboxToEEType); } else { - if (o != null && (TypeCast.IsInstanceOfAny(ptrUnboxToEEType, o) == null)) + if (o != null && (TypeCast.IsInstanceOfAny(pUnboxToEEType, o) == null)) { - throw ptrUnboxToEEType->GetClasslibException(ExceptionIDs.InvalidCast); + throw pUnboxToEEType->GetClasslibException(ExceptionIDs.InvalidCast); } Unsafe.As(ref data) = o; diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs index c1663688004012..bcadcd799b20dd 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs @@ -33,7 +33,7 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhNewObject")] - internal static extern object RhNewObject(EETypePtr pEEType); + internal static extern unsafe object RhNewObject(MethodTable* pEEType); // Move memory which may be on the heap which may have object references in it. // In general, a memcpy on the heap is unsafe, but this is able to perform the diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs index db284d80cce0e3..08b1e6b0d87bf7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/ComparerHelpers.cs @@ -9,38 +9,38 @@ using System.Runtime; using System.Runtime.CompilerServices; +using Internal.Runtime; using Internal.Runtime.Augments; namespace Internal.IntrinsicSupport { internal static class ComparerHelpers { - private static bool ImplementsIComparable(RuntimeTypeHandle t) + private static unsafe bool ImplementsIComparable(RuntimeTypeHandle t) { - EETypePtr objectType = t.ToEETypePtr(); - EETypePtr icomparableType = typeof(IComparable<>).TypeHandle.ToEETypePtr(); - int interfaceCount = objectType.Interfaces.Count; + MethodTable* objectType = t.ToMethodTable(); + MethodTable* icomparableType = typeof(IComparable<>).TypeHandle.ToMethodTable(); + int interfaceCount = objectType->NumInterfaces; for (int i = 0; i < interfaceCount; i++) { - EETypePtr interfaceType = objectType.Interfaces[i]; + MethodTable* interfaceType = objectType->InterfaceMap[i]; - if (!interfaceType.IsGeneric) + if (!interfaceType->IsGeneric) continue; - if (interfaceType.GenericDefinition == icomparableType) + if (interfaceType->GenericDefinition == icomparableType) { - var instantiation = interfaceType.Instantiation; - if (instantiation.Length != 1) + if (interfaceType->GenericArity != 1) continue; - if (objectType.IsValueType) + if (objectType->IsValueType) { - if (instantiation[0] == objectType) + if (interfaceType->GenericArguments[0] == objectType) { return true; } } - else if (RuntimeImports.AreTypesAssignable(objectType, instantiation[0])) + else if (RuntimeImports.AreTypesAssignable(objectType, interfaceType->GenericArguments[0])) { return true; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs index b35bee1fca61d0..94592af9c8784a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/IntrinsicSupport/EqualityComparerHelpers.cs @@ -12,32 +12,31 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; +using Internal.Runtime; using Internal.Runtime.Augments; namespace Internal.IntrinsicSupport { internal static class EqualityComparerHelpers { - private static bool ImplementsIEquatable(RuntimeTypeHandle t) + private static unsafe bool ImplementsIEquatable(RuntimeTypeHandle t) { - EETypePtr objectType = t.ToEETypePtr(); - EETypePtr iequatableType = typeof(IEquatable<>).TypeHandle.ToEETypePtr(); - int interfaceCount = objectType.Interfaces.Count; + MethodTable* objectType = t.ToMethodTable(); + MethodTable* iequatableType = typeof(IEquatable<>).TypeHandle.ToMethodTable(); + int interfaceCount = objectType->NumInterfaces; for (int i = 0; i < interfaceCount; i++) { - EETypePtr interfaceType = objectType.Interfaces[i]; + MethodTable* interfaceType = objectType->InterfaceMap[i]; - if (!interfaceType.IsGeneric) + if (!interfaceType->IsGeneric) continue; - if (interfaceType.GenericDefinition == iequatableType) + if (interfaceType->GenericDefinition == iequatableType) { - var instantiation = interfaceType.Instantiation; - - if (instantiation.Length != 1) + if (interfaceType->GenericArity != 1) continue; - if (instantiation[0] == objectType) + if (interfaceType->GenericArguments[0] == objectType) { return true; } @@ -47,9 +46,9 @@ private static bool ImplementsIEquatable(RuntimeTypeHandle t) return false; } - internal static bool IsEnum(RuntimeTypeHandle t) + internal static unsafe bool IsEnum(RuntimeTypeHandle t) { - return t.ToEETypePtr().IsEnum; + return t.ToMethodTable()->IsEnum; } // this function utilizes the template type loader to generate new diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index cc42a78d9da2ce..4aac720d01142e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -23,6 +23,8 @@ using System.Numerics; using System.Reflection; +using Internal.Runtime; + using EETypeElementType = Internal.Runtime.EETypeElementType; namespace Internal.Reflection.Augments @@ -39,24 +41,24 @@ public static void Initialize(ReflectionCoreCallbacks reflectionCoreCallbacks) s_reflectionCoreCallbacks = reflectionCoreCallbacks; } - internal static TypeCode GetRuntimeTypeCode(RuntimeType type) + internal static unsafe TypeCode GetRuntimeTypeCode(RuntimeType type) { Debug.Assert(type != null); - EETypePtr eeType = type.ToEETypePtrMayBeNull(); - if (eeType.IsNull) + MethodTable* eeType = type.ToMethodTableMayBeNull(); + if (eeType == null) { // Type exists in metadata only. Aside from the enums, there is no chance a type with a TypeCode would not have an MethodTable, // so if it's not an enum, return the default. if (!type.IsActualEnum) return TypeCode.Object; Type underlyingType = Enum.GetUnderlyingType(type); - eeType = underlyingType.TypeHandle.ToEETypePtr(); + eeType = underlyingType.TypeHandle.ToMethodTable(); } // Note: Type.GetTypeCode() is expected to return the underlying type's TypeCode for enums. EETypePtr.CorElementType does the same, // so this one switch handles both cases. - EETypeElementType rhType = eeType.ElementType; + EETypeElementType rhType = eeType->ElementType; switch (rhType) { case EETypeElementType.Boolean: return TypeCode.Boolean; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index c99fd28a8e27cd..d58c1a151bc8e3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -33,7 +33,7 @@ namespace Internal.Runtime.Augments { - public static class RuntimeAugments + public static unsafe class RuntimeAugments { /// /// Callbacks used for metadata-based stack trace resolution. @@ -66,7 +66,7 @@ public static void InitializeStackTraceMetadataSupport(StackTraceMetadataCallbac // public static object RawNewObject(RuntimeTypeHandle typeHandle) { - return RuntimeImports.RhNewObject(typeHandle.ToEETypePtr()); + return RuntimeImports.RhNewObject(typeHandle.ToMethodTable()); } // @@ -75,8 +75,8 @@ public static object RawNewObject(RuntimeTypeHandle typeHandle) public static Array NewArray(RuntimeTypeHandle typeHandleForArrayType, int count) { // Don't make the easy mistake of passing in the element MethodTable rather than the "array of element" MethodTable. - Debug.Assert(typeHandleForArrayType.ToEETypePtr().IsSzArray); - return RuntimeImports.RhNewArray(typeHandleForArrayType.ToEETypePtr(), count); + Debug.Assert(typeHandleForArrayType.ToMethodTable()->IsSzArray); + return RuntimeImports.RhNewArray(typeHandleForArrayType.ToMethodTable(), count); } // @@ -107,8 +107,8 @@ public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArray { // We just checked above that all lower bounds are zero. In that case, we should actually allocate // a new SzArray instead. - Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(typeHandleForArrayType.ToEETypePtr().ArrayElementType))!; - return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), lengths[0]); + Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(typeHandleForArrayType.ToMethodTable()->RelatedParameterType))!; + return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToMethodTable(), lengths[0]); } // Create a local copy of the lengths that cannot be modified by the caller @@ -116,12 +116,12 @@ public static unsafe Array NewMultiDimArray(RuntimeTypeHandle typeHandleForArray for (int i = 0; i < lengths.Length; i++) pImmutableLengths[i] = lengths[i]; - return Array.NewMultiDimArray(typeHandleForArrayType.ToEETypePtr(), pImmutableLengths, lengths.Length); + return Array.NewMultiDimArray(typeHandleForArrayType.ToMethodTable(), pImmutableLengths, lengths.Length); } public static IntPtr GetAllocateObjectHelperForType(RuntimeTypeHandle type) { - return RuntimeImports.RhGetRuntimeHelperForType(type.ToEETypePtr(), RuntimeHelperKind.AllocateObject); + return RuntimeImports.RhGetRuntimeHelperForType(type.ToMethodTable(), RuntimeHelperKind.AllocateObject); } public static IntPtr GetFallbackDefaultConstructor() @@ -134,7 +134,7 @@ public static IntPtr GetFallbackDefaultConstructor() // public static Delegate CreateDelegate(RuntimeTypeHandle typeHandleForDelegate, IntPtr ldftnResult, object thisObject, bool isStatic, bool isOpen) { - return Delegate.CreateDelegate(typeHandleForDelegate.ToEETypePtr(), ldftnResult, thisObject, isStatic: isStatic, isOpen: isOpen); + return Delegate.CreateDelegate(typeHandleForDelegate.ToMethodTable(), ldftnResult, thisObject, isStatic: isStatic, isOpen: isOpen); } // @@ -177,7 +177,7 @@ public static unsafe bool FindBlob(TypeManagerHandle typeManager, int blobId, In public static IntPtr GetPointerFromTypeHandle(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().RawValue; + return (IntPtr)typeHandle.ToMethodTable(); } public static unsafe TypeManagerHandle GetModuleFromTypeHandle(RuntimeTypeHandle typeHandle) @@ -185,14 +185,14 @@ public static unsafe TypeManagerHandle GetModuleFromTypeHandle(RuntimeTypeHandle return typeHandle.ToMethodTable()->TypeManager; } - public static RuntimeTypeHandle CreateRuntimeTypeHandle(IntPtr ldTokenResult) + public static unsafe RuntimeTypeHandle CreateRuntimeTypeHandle(IntPtr ldTokenResult) { - return new RuntimeTypeHandle(new EETypePtr(ldTokenResult)); + return new RuntimeTypeHandle((MethodTable*)ldTokenResult); } public static unsafe void StoreValueTypeField(IntPtr address, object fieldValue, RuntimeTypeHandle fieldType) { - RuntimeImports.RhUnbox(fieldValue, ref *(byte*)address, fieldType.ToEETypePtr()); + RuntimeImports.RhUnbox(fieldValue, ref *(byte*)address, fieldType.ToMethodTable()); } public static unsafe ref byte GetRawData(object obj) @@ -202,7 +202,7 @@ public static unsafe ref byte GetRawData(object obj) public static unsafe object LoadValueTypeField(IntPtr address, RuntimeTypeHandle fieldType) { - return RuntimeImports.RhBox(fieldType.ToEETypePtr(), ref *(byte*)address); + return RuntimeImports.RhBox(fieldType.ToMethodTable(), ref *(byte*)address); } public static unsafe object LoadPointerTypeField(IntPtr address, RuntimeTypeHandle fieldType) @@ -212,19 +212,19 @@ public static unsafe object LoadPointerTypeField(IntPtr address, RuntimeTypeHand public static unsafe void StoreValueTypeField(ref byte address, object fieldValue, RuntimeTypeHandle fieldType) { - RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToEETypePtr()); + RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToMethodTable()); } public static unsafe void StoreValueTypeField(object obj, int fieldOffset, object fieldValue, RuntimeTypeHandle fieldType) { ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); - RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToEETypePtr()); + RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToMethodTable()); } public static unsafe object LoadValueTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType) { ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize)); - return RuntimeImports.RhBox(fieldType.ToEETypePtr(), ref address); + return RuntimeImports.RhBox(fieldType.ToMethodTable(), ref address); } public static unsafe object LoadPointerTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType) @@ -258,24 +258,24 @@ public static object LoadReferenceTypeField(object obj, int fieldOffset) [CLSCompliant(false)] public static void StoreValueTypeFieldValueIntoValueType(TypedReference typedReference, int fieldOffset, object fieldValue, RuntimeTypeHandle fieldTypeHandle) { - Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToMethodTable()->IsValueType); - RuntimeImports.RhUnbox(fieldValue, ref Unsafe.Add(ref typedReference.Value, fieldOffset), fieldTypeHandle.ToEETypePtr()); + RuntimeImports.RhUnbox(fieldValue, ref Unsafe.Add(ref typedReference.Value, fieldOffset), fieldTypeHandle.ToMethodTable()); } [CLSCompliant(false)] public static object LoadValueTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) { - Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); - Debug.Assert(fieldTypeHandle.ToEETypePtr().IsValueType); + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToMethodTable()->IsValueType); + Debug.Assert(fieldTypeHandle.ToMethodTable()->IsValueType); - return RuntimeImports.RhBox(fieldTypeHandle.ToEETypePtr(), ref Unsafe.Add(ref typedReference.Value, fieldOffset)); + return RuntimeImports.RhBox(fieldTypeHandle.ToMethodTable(), ref Unsafe.Add(ref typedReference.Value, fieldOffset)); } [CLSCompliant(false)] public static void StoreReferenceTypeFieldValueIntoValueType(TypedReference typedReference, int fieldOffset, object fieldValue) { - Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToMethodTable()->IsValueType); Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)) = fieldValue; } @@ -283,7 +283,7 @@ public static void StoreReferenceTypeFieldValueIntoValueType(TypedReference type [CLSCompliant(false)] public static object LoadReferenceTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset) { - Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToMethodTable()->IsValueType); return Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); } @@ -291,8 +291,8 @@ public static object LoadReferenceTypeFieldValueFromValueType(TypedReference typ [CLSCompliant(false)] public static unsafe object LoadPointerTypeFieldValueFromValueType(TypedReference typedReference, int fieldOffset, RuntimeTypeHandle fieldTypeHandle) { - Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToEETypePtr().IsValueType); - Debug.Assert(fieldTypeHandle.ToEETypePtr().IsPointer); + Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToMethodTable()->IsValueType); + Debug.Assert(fieldTypeHandle.ToMethodTable()->IsPointer); IntPtr ptrValue = Unsafe.As(ref Unsafe.Add(ref typedReference.Value, fieldOffset)); return ReflectionPointer.Box((void*)ptrValue, Type.GetTypeFromHandle(fieldTypeHandle)); @@ -309,7 +309,7 @@ public static int GetHighestStaticThreadStaticIndex(TypeManagerHandle typeManage return length / IntPtr.Size; } - public static unsafe int ObjectHeaderSize => sizeof(EETypePtr); + public static unsafe int ObjectHeaderSize => sizeof(ObjHeader); public static unsafe void EnsureClassConstructorRun(IntPtr staticClassConstructionContext) { @@ -319,9 +319,9 @@ public static unsafe void EnsureClassConstructorRun(IntPtr staticClassConstructi public static Type GetEnumUnderlyingType(RuntimeTypeHandle enumTypeHandle) { - Debug.Assert(enumTypeHandle.ToEETypePtr().IsEnum); + Debug.Assert(enumTypeHandle.ToMethodTable()->IsEnum); - EETypeElementType elementType = enumTypeHandle.ToEETypePtr().ElementType; + EETypeElementType elementType = enumTypeHandle.ToMethodTable()->ElementType; switch (elementType) { case EETypeElementType.Boolean: @@ -351,7 +351,7 @@ public static Type GetEnumUnderlyingType(RuntimeTypeHandle enumTypeHandle) public static RuntimeTypeHandle GetRelatedParameterTypeHandle(RuntimeTypeHandle parameterTypeHandle) { - EETypePtr elementType = parameterTypeHandle.ToEETypePtr().ArrayElementType; + MethodTable* elementType = parameterTypeHandle.ToMethodTable()->RelatedParameterType; return new RuntimeTypeHandle(elementType); } @@ -363,17 +363,17 @@ public static unsafe int GetArrayRankOrMinusOneForSzArray(RuntimeTypeHandle arra public static bool IsValueType(RuntimeTypeHandle type) { - return type.ToEETypePtr().IsValueType; + return type.ToMethodTable()->IsValueType; } public static bool IsInterface(RuntimeTypeHandle type) { - return type.ToEETypePtr().IsInterface; + return type.ToMethodTable()->IsInterface; } public static unsafe object Box(RuntimeTypeHandle type, IntPtr address) { - return RuntimeImports.RhBox(type.ToEETypePtr(), ref *(byte*)address); + return RuntimeImports.RhBox(type.ToMethodTable(), ref *(byte*)address); } // Used to mutate the first parameter in a closed static delegate. Note that this does no synchronization of any kind; @@ -404,8 +404,8 @@ public static RuntimeTypeHandle ProjectionTypeForArrays // public static bool IsAssignableFrom(RuntimeTypeHandle dstType, RuntimeTypeHandle srcType) { - EETypePtr dstEEType = dstType.ToEETypePtr(); - EETypePtr srcEEType = srcType.ToEETypePtr(); + MethodTable* dstEEType = dstType.ToMethodTable(); + MethodTable* srcEEType = srcType.ToMethodTable(); return RuntimeImports.AreTypesAssignable(srcEEType, dstEEType); } @@ -418,44 +418,39 @@ public static bool IsAssignableFrom(RuntimeTypeHandle dstType, RuntimeTypeHandle // public static bool TryGetBaseType(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle baseTypeHandle) { - EETypePtr eeType = typeHandle.ToEETypePtr(); - if (eeType.IsGenericTypeDefinition || eeType.IsPointer || eeType.IsByRef || eeType.IsFunctionPointer) + MethodTable* eeType = typeHandle.ToMethodTable(); + if (eeType->IsGenericTypeDefinition || eeType->IsPointer || eeType->IsByRef || eeType->IsFunctionPointer) { baseTypeHandle = default(RuntimeTypeHandle); return false; } - baseTypeHandle = new RuntimeTypeHandle(eeType.BaseType); + baseTypeHandle = new RuntimeTypeHandle(eeType->BaseType); return true; } public static int GetGCDescSize(RuntimeTypeHandle typeHandle) { - return RuntimeImports.RhGetGCDescSize(typeHandle.ToEETypePtr()); + return RuntimeImports.RhGetGCDescSize(typeHandle.ToMethodTable()); } public static int GetInterfaceCount(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().Interfaces.Count; + return typeHandle.ToMethodTable()->NumInterfaces; } public static RuntimeTypeHandle GetInterface(RuntimeTypeHandle typeHandle, int index) { - return new RuntimeTypeHandle(typeHandle.ToEETypePtr().Interfaces[index]); + return new RuntimeTypeHandle(typeHandle.ToMethodTable()->InterfaceMap[index]); } public static IntPtr NewInterfaceDispatchCell(RuntimeTypeHandle interfaceTypeHandle, int slotNumber) { - IntPtr cell = RuntimeImports.RhNewInterfaceDispatchCell(interfaceTypeHandle.ToEETypePtr(), slotNumber); + IntPtr cell = RuntimeImports.RhNewInterfaceDispatchCell(interfaceTypeHandle.ToMethodTable(), slotNumber); if (cell == IntPtr.Zero) throw new OutOfMemoryException(); return cell; } - public static int GetValueTypeSize(RuntimeTypeHandle typeHandle) - { - return (int)typeHandle.ToEETypePtr().ValueTypeSize; - } - [Intrinsic] public static RuntimeTypeHandle GetCanonType(CanonTypeKind kind) { @@ -465,65 +460,65 @@ public static RuntimeTypeHandle GetCanonType(CanonTypeKind kind) public static RuntimeTypeHandle GetGenericDefinition(RuntimeTypeHandle typeHandle) { - EETypePtr eeType = typeHandle.ToEETypePtr(); - Debug.Assert(eeType.IsGeneric); - return new RuntimeTypeHandle(eeType.GenericDefinition); + MethodTable* eeType = typeHandle.ToMethodTable(); + Debug.Assert(eeType->IsGeneric); + return new RuntimeTypeHandle(eeType->GenericDefinition); } public static RuntimeTypeHandle GetGenericArgument(RuntimeTypeHandle typeHandle, int argumentIndex) { - EETypePtr eeType = typeHandle.ToEETypePtr(); - Debug.Assert(eeType.IsGeneric); - return new RuntimeTypeHandle(eeType.Instantiation[argumentIndex]); + MethodTable* eeType = typeHandle.ToMethodTable(); + Debug.Assert(eeType->IsGeneric); + return new RuntimeTypeHandle(eeType->GenericArguments[argumentIndex]); } public static RuntimeTypeHandle GetGenericInstantiation(RuntimeTypeHandle typeHandle, out RuntimeTypeHandle[] genericTypeArgumentHandles) { - EETypePtr eeType = typeHandle.ToEETypePtr(); + MethodTable* eeType = typeHandle.ToMethodTable(); - Debug.Assert(eeType.IsGeneric); + Debug.Assert(eeType->IsGeneric); - var instantiation = eeType.Instantiation; - genericTypeArgumentHandles = new RuntimeTypeHandle[instantiation.Length]; - for (int i = 0; i < instantiation.Length; i++) + MethodTableList instantiation = eeType->GenericArguments; + genericTypeArgumentHandles = new RuntimeTypeHandle[eeType->GenericArity]; + for (int i = 0; i < genericTypeArgumentHandles.Length; i++) { genericTypeArgumentHandles[i] = new RuntimeTypeHandle(instantiation[i]); } - return new RuntimeTypeHandle(eeType.GenericDefinition); + return new RuntimeTypeHandle(eeType->GenericDefinition); } public static bool IsGenericType(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsGeneric; + return typeHandle.ToMethodTable()->IsGeneric; } public static bool IsArrayType(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsArray; + return typeHandle.ToMethodTable()->IsArray; } - public static bool IsByRefLike(RuntimeTypeHandle typeHandle) => typeHandle.ToEETypePtr().IsByRefLike; + public static bool IsByRefLike(RuntimeTypeHandle typeHandle) => typeHandle.ToMethodTable()->IsByRefLike; public static bool IsDynamicType(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsDynamicType; + return typeHandle.ToMethodTable()->IsDynamicType; } public static bool HasCctor(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().HasCctor; + return typeHandle.ToMethodTable()->HasCctor; } public static IntPtr ResolveDispatchOnType(RuntimeTypeHandle instanceType, RuntimeTypeHandle interfaceType, int slot) { - return RuntimeImports.RhResolveDispatchOnType(instanceType.ToEETypePtr(), interfaceType.ToEETypePtr(), checked((ushort)slot)); + return RuntimeImports.RhResolveDispatchOnType(instanceType.ToMethodTable(), interfaceType.ToMethodTable(), checked((ushort)slot)); } public static unsafe IntPtr ResolveStaticDispatchOnType(RuntimeTypeHandle instanceType, RuntimeTypeHandle interfaceType, int slot, out RuntimeTypeHandle genericContext) { - EETypePtr genericContextPtr = default; - IntPtr result = RuntimeImports.RhResolveDispatchOnType(instanceType.ToEETypePtr(), interfaceType.ToEETypePtr(), checked((ushort)slot), &genericContextPtr); + MethodTable* genericContextPtr = default; + IntPtr result = RuntimeImports.RhResolveDispatchOnType(instanceType.ToMethodTable(), interfaceType.ToMethodTable(), checked((ushort)slot), &genericContextPtr); if (result != IntPtr.Zero) genericContext = new RuntimeTypeHandle(genericContextPtr); else @@ -533,17 +528,17 @@ public static unsafe IntPtr ResolveStaticDispatchOnType(RuntimeTypeHandle instan public static IntPtr ResolveDispatch(object instance, RuntimeTypeHandle interfaceType, int slot) { - return RuntimeImports.RhResolveDispatch(instance, interfaceType.ToEETypePtr(), checked((ushort)slot)); + return RuntimeImports.RhResolveDispatch(instance, interfaceType.ToMethodTable(), checked((ushort)slot)); } public static bool IsUnmanagedPointerType(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsPointer; + return typeHandle.ToMethodTable()->IsPointer; } public static bool IsFunctionPointerType(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsFunctionPointer; + return typeHandle.ToMethodTable()->IsFunctionPointer; } public static unsafe RuntimeTypeHandle GetFunctionPointerReturnType(RuntimeTypeHandle typeHandle) @@ -585,12 +580,12 @@ public static unsafe bool IsUnmanagedFunctionPointerType(RuntimeTypeHandle typeH public static bool IsByRefType(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsByRef; + return typeHandle.ToMethodTable()->IsByRef; } public static bool IsGenericTypeDefinition(RuntimeTypeHandle typeHandle) { - return typeHandle.ToEETypePtr().IsGenericTypeDefinition; + return typeHandle.ToMethodTable()->IsGenericTypeDefinition; } // @@ -598,43 +593,43 @@ public static bool IsGenericTypeDefinition(RuntimeTypeHandle typeHandle) // public static bool CanPrimitiveWiden(RuntimeTypeHandle srcType, RuntimeTypeHandle dstType) { - EETypePtr srcEEType = srcType.ToEETypePtr(); - EETypePtr dstEEType = dstType.ToEETypePtr(); + MethodTable* srcEEType = srcType.ToMethodTable(); + MethodTable* dstEEType = dstType.ToMethodTable(); - if (srcEEType.IsGenericTypeDefinition || dstEEType.IsGenericTypeDefinition) + if (srcEEType->IsGenericTypeDefinition || dstEEType->IsGenericTypeDefinition) return false; - if (srcEEType.IsPointer || dstEEType.IsPointer) + if (srcEEType->IsPointer || dstEEType->IsPointer) return false; - if (srcEEType.IsFunctionPointer || dstEEType.IsFunctionPointer) + if (srcEEType->IsFunctionPointer || dstEEType->IsFunctionPointer) return false; - if (srcEEType.IsByRef || dstEEType.IsByRef) + if (srcEEType->IsByRef || dstEEType->IsByRef) return false; - if (!srcEEType.IsPrimitive) + if (!srcEEType->IsPrimitive) return false; - if (!dstEEType.IsPrimitive) + if (!dstEEType->IsPrimitive) return false; - if (!srcEEType.CorElementTypeInfo.CanWidenTo(dstEEType.CorElementType)) + if (!new EETypePtr(srcEEType).CorElementTypeInfo.CanWidenTo(new EETypePtr(dstEEType).CorElementType)) return false; return true; } public static object CheckArgument(object srcObject, RuntimeTypeHandle dstType, BinderBundle? binderBundle) { - return InvokeUtils.CheckArgument(srcObject, dstType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle); + return InvokeUtils.CheckArgument(srcObject, dstType.ToMethodTable(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle); } // FieldInfo.SetValueDirect() has a completely different set of rules on how to coerce the argument from // the other Reflection api. public static object CheckArgumentForDirectFieldAccess(object srcObject, RuntimeTypeHandle dstType) { - return InvokeUtils.CheckArgument(srcObject, dstType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.SetFieldDirect, binderBundle: null); + return InvokeUtils.CheckArgument(srcObject, dstType.ToMethodTable(), InvokeUtils.CheckArgumentSemantics.SetFieldDirect, binderBundle: null); } public static bool IsAssignable(object srcObject, RuntimeTypeHandle dstType) { - EETypePtr srcEEType = srcObject.GetEETypePtr(); - return RuntimeImports.AreTypesAssignable(srcEEType, dstType.ToEETypePtr()); + MethodTable* srcEEType = srcObject.GetMethodTable(); + return RuntimeImports.AreTypesAssignable(srcEEType, dstType.ToMethodTable()); } //============================================================================================== @@ -642,12 +637,12 @@ public static bool IsAssignable(object srcObject, RuntimeTypeHandle dstType) //============================================================================================== public static bool IsNullable(RuntimeTypeHandle declaringTypeHandle) { - return declaringTypeHandle.ToEETypePtr().IsNullable; + return declaringTypeHandle.ToMethodTable()->IsNullable; } public static RuntimeTypeHandle GetNullableType(RuntimeTypeHandle nullableType) { - EETypePtr theT = nullableType.ToEETypePtr().NullableType; + MethodTable* theT = nullableType.ToMethodTable()->NullableType; return new RuntimeTypeHandle(theT); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs index 31d845248548c0..c086019f5041cb 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ArrayHelpers.cs @@ -18,17 +18,16 @@ internal static class ArrayHelpers /// Helper for array allocations via `newobj` IL instruction. Dimensions are passed in as block of integers. /// The content of the dimensions block may be modified by the helper. /// - public static unsafe Array NewObjArray(IntPtr pEEType, int nDimensions, int* pDimensions) + public static unsafe Array NewObjArray(MethodTable* pEEType, int nDimensions, int* pDimensions) { - EETypePtr eeType = new EETypePtr(pEEType); - Debug.Assert(eeType.IsArray && !eeType.IsSzArray); + Debug.Assert(pEEType->IsArray && !pEEType->IsSzArray); Debug.Assert(nDimensions > 0); // Rank 1 arrays are handled below. - Debug.Assert(eeType.ArrayRank > 1); + Debug.Assert(pEEType->ArrayRank > 1); // Multidimensional arrays have two ctors, one with and one without lower bounds - int rank = eeType.ArrayRank; + int rank = pEEType->ArrayRank; Debug.Assert(rank == nDimensions || 2 * rank == nDimensions); if (rank < nDimensions) @@ -42,7 +41,7 @@ public static unsafe Array NewObjArray(IntPtr pEEType, int nDimensions, int* pDi } } - return Array.NewMultiDimArray(eeType, pDimensions, rank); + return Array.NewMultiDimArray(pEEType, pDimensions, rank); } /// @@ -51,27 +50,26 @@ public static unsafe Array NewObjArray(IntPtr pEEType, int nDimensions, int* pDi /// [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "The compiler ensures that if we have a TypeHandle of a Rank-1 MdArray, we also generated the SzArray.")] - public static unsafe Array NewObjArrayRare(IntPtr pEEType, int nDimensions, int* pDimensions) + public static unsafe Array NewObjArrayRare(MethodTable* pEEType, int nDimensions, int* pDimensions) { - EETypePtr eeType = new EETypePtr(pEEType); - Debug.Assert(eeType.IsArray); + Debug.Assert(pEEType->IsArray); Debug.Assert(nDimensions > 0); - Debug.Assert(eeType.ArrayRank == 1); + Debug.Assert(pEEType->ArrayRank == 1); - if (eeType.IsSzArray) + if (pEEType->IsSzArray) { - Array ret = RuntimeImports.RhNewArray(eeType, pDimensions[0]); + Array ret = RuntimeImports.RhNewArray(pEEType, pDimensions[0]); if (nDimensions > 1) { // Jagged arrays have constructor for each possible depth - EETypePtr elementType = eeType.ArrayElementType; - Debug.Assert(elementType.IsSzArray); + MethodTable* elementType = pEEType->RelatedParameterType; + Debug.Assert(elementType->IsSzArray); Array[] arrayOfArrays = (Array[])ret; for (int i = 0; i < arrayOfArrays.Length; i++) - arrayOfArrays[i] = NewObjArrayRare(elementType.RawValue, nDimensions - 1, pDimensions + 1); + arrayOfArrays[i] = NewObjArrayRare(elementType, nDimensions - 1, pDimensions + 1); } return ret; @@ -92,8 +90,8 @@ public static unsafe Array NewObjArrayRare(IntPtr pEEType, int nDimensions, int* // Multidimensional array of rank 1 with 0 lower bounds gets actually allocated // as an SzArray. SzArray is castable to MdArray rank 1. - Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(eeType.ArrayElementType))!; - return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), pDimensions[0]); + Type elementType = Type.GetTypeFromHandle(new RuntimeTypeHandle(pEEType->RelatedParameterType))!; + return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToMethodTable(), pDimensions[0]); } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs index f6c921ad22d65b..9f597e151d550f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs @@ -488,10 +488,10 @@ public static object ConvertNativeComInterfaceToManaged(IntPtr pUnk) [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "This API will be called from compiler generated code only.")] - internal static int AsAnyGetNativeSize(object o) + internal static unsafe int AsAnyGetNativeSize(object o) { // Array, string and StringBuilder are not implemented. - if (o.GetEETypePtr().IsArray || + if (o.GetMethodTable()->IsArray || o is string || o is StringBuilder) { @@ -504,10 +504,10 @@ o is string || [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "This API will be called from compiler generated code only.")] - internal static void AsAnyMarshalManagedToNative(object o, IntPtr address) + internal static unsafe void AsAnyMarshalManagedToNative(object o, IntPtr address) { // Array, string and StringBuilder are not implemented. - if (o.GetEETypePtr().IsArray || + if (o.GetMethodTable()->IsArray || o is string || o is StringBuilder) { @@ -517,10 +517,10 @@ o is string || Marshal.StructureToPtr(o, address, fDeleteOld: false); } - internal static void AsAnyMarshalNativeToManaged(IntPtr address, object o) + internal static unsafe void AsAnyMarshalNativeToManaged(IntPtr address, object o) { // Array, string and StringBuilder are not implemented. - if (o.GetEETypePtr().IsArray || + if (o.GetMethodTable()->IsArray || o is string || o is StringBuilder) { @@ -532,10 +532,10 @@ o is string || [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "This API will be called from compiler generated code only.")] - internal static void AsAnyCleanupNative(IntPtr address, object o) + internal static unsafe void AsAnyCleanupNative(IntPtr address, object o) { // Array, string and StringBuilder are not implemented. - if (o.GetEETypePtr().IsArray || + if (o.GetMethodTable()->IsArray || o is string || o is StringBuilder) { @@ -591,7 +591,7 @@ public static unsafe object InitializeCustomMarshaller(RuntimeTypeHandle pParame throw new ApplicationException(); } - if (!RuntimeImports.AreTypesAssignable(pMarshallerType.ToEETypePtr(), EETypePtr.EETypePtrOf())) + if (!RuntimeImports.AreTypesAssignable(pMarshallerType.ToMethodTable(), MethodTable.Of())) { throw new ApplicationException(); } @@ -602,7 +602,7 @@ public static unsafe object InitializeCustomMarshaller(RuntimeTypeHandle pParame throw new ApplicationException(); } - if (!RuntimeImports.AreTypesAssignable(marshaller.GetEETypePtr(), EETypePtr.EETypePtrOf())) + if (!RuntimeImports.AreTypesAssignable(marshaller.GetMethodTable(), MethodTable.Of())) { throw new ApplicationException(); } @@ -615,7 +615,7 @@ internal unsafe struct ModuleFixupCell { public IntPtr Handle; public IntPtr ModuleName; - public EETypePtr CallingAssemblyType; + public MethodTable* CallingAssemblyType; public uint DllImportSearchPathAndCookie; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs index 88f98794c5db5e..4ac17b83b5b00d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/LdTokenHelpers.cs @@ -12,9 +12,9 @@ namespace Internal.Runtime.CompilerHelpers /// internal static class LdTokenHelpers { - private static RuntimeTypeHandle GetRuntimeTypeHandle(IntPtr pEEType) + private static unsafe RuntimeTypeHandle GetRuntimeTypeHandle(MethodTable* pEEType) { - return new RuntimeTypeHandle(new EETypePtr(pEEType)); + return new RuntimeTypeHandle(pEEType); } private static unsafe RuntimeMethodHandle GetRuntimeMethodHandle(IntPtr pHandleSignature) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs index d3f21f5b6da988..1d4f979ced8545 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/OpenMethodResolver.cs @@ -19,10 +19,10 @@ namespace Internal.Runtime.CompilerServices // so that repeated allocation of the same resolver will not leak. // 3) Use the ResolveMethod function to do the virtual lookup. This function takes advantage of // a lockless cache so the resolution is very fast for repeated lookups. - public struct OpenMethodResolver : IEquatable + public unsafe struct OpenMethodResolver : IEquatable { // Lazy initialized to point to the type loader method when the first `GVMResolve` resolver is created - private static unsafe delegate* s_lazyGvmLookupForSlot; + private static delegate* s_lazyGvmLookupForSlot; public const short DispatchResolve = 0; public const short GVMResolve = 1; @@ -34,23 +34,23 @@ public struct OpenMethodResolver : IEquatable private readonly int _handle; private readonly IntPtr _methodHandleOrSlotOrCodePointer; private readonly IntPtr _nonVirtualOpenInvokeCodePointer; - private readonly EETypePtr _declaringType; + private readonly MethodTable* _declaringType; public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, int slot, GCHandle readerGCHandle, int handle) { _resolveType = DispatchResolve; - _declaringType = declaringTypeOfSlot.ToEETypePtr(); + _declaringType = declaringTypeOfSlot.ToMethodTable(); _methodHandleOrSlotOrCodePointer = new IntPtr(slot); _handle = handle; _readerGCHandle = readerGCHandle; _nonVirtualOpenInvokeCodePointer = IntPtr.Zero; } - public unsafe OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle) + public OpenMethodResolver(RuntimeTypeHandle declaringTypeOfSlot, RuntimeMethodHandle gvmSlot, GCHandle readerGCHandle, int handle) { _resolveType = GVMResolve; _methodHandleOrSlotOrCodePointer = *(IntPtr*)&gvmSlot; - _declaringType = declaringTypeOfSlot.ToEETypePtr(); + _declaringType = declaringTypeOfSlot.ToMethodTable(); _handle = handle; _readerGCHandle = readerGCHandle; _nonVirtualOpenInvokeCodePointer = IntPtr.Zero; @@ -63,7 +63,7 @@ public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, G { _resolveType = OpenNonVirtualResolve; _nonVirtualOpenInvokeCodePointer = _methodHandleOrSlotOrCodePointer = codePointer; - _declaringType = declaringType.ToEETypePtr(); + _declaringType = declaringType.ToMethodTable(); _handle = handle; _readerGCHandle = readerGCHandle; } @@ -72,7 +72,7 @@ public OpenMethodResolver(RuntimeTypeHandle declaringType, IntPtr codePointer, G { _resolveType = resolveType; _methodHandleOrSlotOrCodePointer = codePointer; - _declaringType = declaringType.ToEETypePtr(); + _declaringType = declaringType.ToMethodTable(); _handle = handle; _readerGCHandle = readerGCHandle; if (resolveType == OpenNonVirtualResolve) @@ -99,7 +99,7 @@ public RuntimeTypeHandle DeclaringType } } - public unsafe RuntimeMethodHandle GVMMethodHandle + public RuntimeMethodHandle GVMMethodHandle { get { @@ -148,7 +148,7 @@ public int Handle } } - private unsafe IntPtr ResolveMethod(object thisObject) + private IntPtr ResolveMethod(object thisObject) { if (_resolveType == DispatchResolve) { @@ -164,12 +164,12 @@ private unsafe IntPtr ResolveMethod(object thisObject) } } - internal static unsafe IntPtr ResolveMethodWorker(IntPtr resolver, object thisObject) + internal static IntPtr ResolveMethodWorker(IntPtr resolver, object thisObject) { return ((OpenMethodResolver*)resolver)->ResolveMethod(thisObject); } - public static unsafe IntPtr ResolveMethod(IntPtr resolver, object thisObject) + public static IntPtr ResolveMethod(IntPtr resolver, object thisObject) { IntPtr nonVirtualOpenInvokeCodePointer = ((OpenMethodResolver*)resolver)->_nonVirtualOpenInvokeCodePointer; if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero) @@ -178,14 +178,14 @@ public static unsafe IntPtr ResolveMethod(IntPtr resolver, object thisObject) return TypeLoaderExports.OpenInstanceMethodLookup(resolver, thisObject); } - public static unsafe IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisType) + public static IntPtr ResolveMethod(IntPtr resolverPtr, RuntimeTypeHandle thisType) { OpenMethodResolver* resolver = ((OpenMethodResolver*)resolverPtr); IntPtr nonVirtualOpenInvokeCodePointer = resolver->_nonVirtualOpenInvokeCodePointer; if (nonVirtualOpenInvokeCodePointer != IntPtr.Zero) return nonVirtualOpenInvokeCodePointer; - return RuntimeImports.RhResolveDispatchOnType(thisType.ToEETypePtr(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer); + return RuntimeImports.RhResolveDispatchOnType(thisType.ToMethodTable(), resolver->_declaringType, (ushort)resolver->_methodHandleOrSlotOrCodePointer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -214,7 +214,7 @@ private static int CalcHashCode(int hashCode1, int hashCode2, int hashCode3, int public override int GetHashCode() { - return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType.IsNull ? 0 : _declaringType.GetHashCode()); + return CalcHashCode(_resolveType, _handle, _methodHandleOrSlotOrCodePointer.GetHashCode(), _declaringType == null ? 0 : (int)_declaringType->HashCode); } public bool Equals(OpenMethodResolver other) @@ -228,7 +228,7 @@ public bool Equals(OpenMethodResolver other) if (other._methodHandleOrSlotOrCodePointer != _methodHandleOrSlotOrCodePointer) return false; - return other._declaringType.Equals(_declaringType); + return other._declaringType == _declaringType; } public override bool Equals(object? obj) @@ -243,7 +243,7 @@ public override bool Equals(object? obj) private static LowLevelDictionary s_internedResolverHash = new LowLevelDictionary(); - public unsafe IntPtr ToIntPtr() + public IntPtr ToIntPtr() { lock (s_internedResolverHash) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs index 6be052ef4c0453..54878128ff9823 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs @@ -28,7 +28,7 @@ internal static IntPtr IDynamicCastableGetInterfaceImplementation(IDynamicInterf { ThrowInvalidOperationException(implType); } - IntPtr result = RuntimeImports.RhResolveDispatchOnType(new EETypePtr(implType), new EETypePtr(interfaceType), slot); + IntPtr result = RuntimeImports.RhResolveDispatchOnType(implType, interfaceType, slot); if (result == IntPtr.Zero) { IDynamicCastableGetInterfaceImplementationFailure(instance, interfaceType, implType); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs index 2c58b4c1aa2283..235a5fad23dd0e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs @@ -137,7 +137,7 @@ private static unsafe object AllocateThreadStaticStorageForType(TypeManagerHandl gcDesc = Internal.Runtime.Augments.RuntimeAugments.TypeLoaderCallbacks.GetThreadStaticGCDescForDynamicType(typeManager, typeTlsIndex); } - return RuntimeImports.RhNewObject(new EETypePtr(gcDesc)); + return RuntimeImports.RhNewObject((MethodTable*)gcDesc); } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.NativeAot.cs index bb98e9b9c8ec37..c15fd13580e1bb 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Activator.NativeAot.cs @@ -15,6 +15,7 @@ using System.Runtime.Remoting; using Internal.Reflection.Augments; +using Internal.Runtime; using Internal.Runtime.CompilerServices; namespace System @@ -52,7 +53,7 @@ public static partial class Activator { // Grab a pointer to the optimized allocator for the type and call it. IntPtr allocator = AllocatorOf(); - t = RawCalliHelper.Call(allocator, EETypePtr.EETypePtrOf().RawValue); + t = RawCalliHelper.Call(allocator, (nint)MethodTable.Of()); RawCalliHelper.Call(defaultConstructor, t); // Debugger goo so that stepping in works. Only affects debug info generation. 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 cefa694e4081d6..84d6fea959a16d 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 @@ -79,7 +79,7 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in if (rank == 1) { - return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToEETypePtr(), pLengths[0]); + return RuntimeImports.RhNewArray(elementType.MakeArrayType().TypeHandle.ToMethodTable(), pLengths[0]); } else { @@ -90,7 +90,7 @@ private static unsafe Array InternalCreate(RuntimeType elementType, int rank, in for (int i = 0; i < rank; i++) pImmutableLengths[i] = pLengths[i]; - return NewMultiDimArray(arrayType.TypeHandle.ToEETypePtr(), pImmutableLengths, rank); + return NewMultiDimArray(arrayType.TypeHandle.ToMethodTable(), pImmutableLengths, rank); } } @@ -113,13 +113,13 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i } } - EETypePtr eeType = arrayType.TypeHandle.ToEETypePtr(); + MethodTable* eeType = arrayType.TypeHandle.ToMethodTable(); if (rank == 1) { // Multidimensional array of rank 1 with 0 lower bounds gets actually allocated // as an SzArray. SzArray is castable to MdArray rank 1. - if (!eeType.IsSzArray) - eeType = arrayType.GetElementType().MakeArrayType().TypeHandle.ToEETypePtr(); + if (!eeType->IsSzArray) + eeType = arrayType.GetElementType().MakeArrayType().TypeHandle.ToMethodTable(); return RuntimeImports.RhNewArray(eeType, pLengths[0]); } @@ -136,8 +136,8 @@ private static unsafe Array InternalCreateFromArrayType(RuntimeType arrayType, i public unsafe void Initialize() { - EETypePtr pElementEEType = ElementEEType; - if (!pElementEEType.IsValueType) + MethodTable* pElementEEType = ElementMethodTable; + if (!pElementEEType->IsValueType) return; IntPtr constructorEntryPoint = RuntimeAugments.TypeLoaderCallbacks.TryGetDefaultConstructorForType(new RuntimeTypeHandle(pElementEEType)); @@ -205,12 +205,12 @@ 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)); - EETypePtr sourceElementEEType = sourceArray.ElementEEType; - EETypePtr destinationElementEEType = destinationArray.ElementEEType; + MethodTable* sourceElementEEType = sourceArray.ElementMethodTable; + MethodTable* destinationElementEEType = destinationArray.ElementMethodTable; - if (!destinationElementEEType.IsValueType && !destinationElementEEType.IsPointer && !destinationElementEEType.IsFunctionPointer) + if (!destinationElementEEType->IsValueType && !destinationElementEEType->IsPointer && !destinationElementEEType->IsFunctionPointer) { - if (!sourceElementEEType.IsValueType && !sourceElementEEType.IsPointer && !sourceElementEEType.IsFunctionPointer) + if (!sourceElementEEType->IsValueType && !sourceElementEEType->IsPointer && !sourceElementEEType->IsFunctionPointer) { CopyImplGcRefArray(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); } @@ -227,7 +227,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de { if (sourceElementEEType == destinationElementEEType) { - if (sourceElementEEType.ContainsGCPointers) + if (sourceElementEEType->ContainsGCPointers) { CopyImplValueTypeArrayWithInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); } @@ -236,7 +236,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); } } - else if ((sourceElementEEType.IsPointer || sourceElementEEType.IsFunctionPointer) && (destinationElementEEType.IsPointer || destinationElementEEType.IsFunctionPointer)) + else if ((sourceElementEEType->IsPointer || sourceElementEEType->IsFunctionPointer) && (destinationElementEEType->IsPointer || destinationElementEEType->IsFunctionPointer)) { // 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*[]). @@ -248,9 +248,9 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de { CopyImplReferenceArrayToValueTypeArray(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable); } - else if (sourceElementEEType.IsPrimitive && destinationElementEEType.IsPrimitive) + else if (sourceElementEEType->IsPrimitive && destinationElementEEType->IsPrimitive) { - if (RuntimeImports.AreTypesAssignable(sourceArray.GetEETypePtr(), destinationArray.GetEETypePtr())) + if (RuntimeImports.AreTypesAssignable(sourceArray.GetMethodTable(), destinationArray.GetMethodTable())) { // If we're okay casting between these two, we're also okay blitting the values over CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); @@ -269,9 +269,9 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de } } - private static bool IsSourceElementABaseClassOrInterfaceOfDestinationValueType(EETypePtr sourceElementEEType, EETypePtr destinationElementEEType) + private static unsafe bool IsSourceElementABaseClassOrInterfaceOfDestinationValueType(MethodTable* sourceElementEEType, MethodTable* destinationElementEEType) { - if (sourceElementEEType.IsValueType || sourceElementEEType.IsPointer || sourceElementEEType.IsFunctionPointer) + if (sourceElementEEType->IsValueType || sourceElementEEType->IsPointer || sourceElementEEType->IsFunctionPointer) return false; // It may look like we're passing the arguments to AreTypesAssignable in the wrong order but we're not. The source array is an interface or Object array, the destination @@ -289,11 +289,11 @@ private static unsafe void CopyImplGcRefArray(Array sourceArray, int sourceIndex { // For mismatched array types, the desktop Array.Copy has a policy that determines whether to throw an ArrayTypeMismatch without any attempt to copy // or to throw an InvalidCastException in the middle of a copy. This code replicates that policy. - EETypePtr sourceElementEEType = sourceArray.ElementEEType; - EETypePtr destinationElementEEType = destinationArray.ElementEEType; + MethodTable* sourceElementEEType = sourceArray.ElementMethodTable; + MethodTable* destinationElementEEType = destinationArray.ElementMethodTable; - Debug.Assert(!sourceElementEEType.IsValueType && !sourceElementEEType.IsPointer && !sourceElementEEType.IsFunctionPointer); - Debug.Assert(!destinationElementEEType.IsValueType && !destinationElementEEType.IsPointer && !destinationElementEEType.IsFunctionPointer); + Debug.Assert(!sourceElementEEType->IsValueType && !sourceElementEEType->IsPointer && !sourceElementEEType->IsFunctionPointer); + Debug.Assert(!destinationElementEEType->IsValueType && !destinationElementEEType->IsPointer && !destinationElementEEType->IsFunctionPointer); bool attemptCopy = RuntimeImports.AreTypesAssignable(sourceElementEEType, destinationElementEEType); bool mustCastCheckEachElement = !attemptCopy; @@ -309,8 +309,8 @@ private static unsafe void CopyImplGcRefArray(Array sourceArray, int sourceIndex // If either array is an interface array, we allow the attempt to copy even if the other element type does not statically implement the interface. // We don't have an "IsInterface" property in EETypePtr so we instead check for a null BaseType. The only the other MethodTable with a null BaseType is // System.Object but if that were the case, we would already have passed one of the AreTypesAssignable checks above. - attemptCopy = attemptCopy || sourceElementEEType.BaseType.IsNull; - attemptCopy = attemptCopy || destinationElementEEType.BaseType.IsNull; + attemptCopy = attemptCopy || sourceElementEEType->BaseType == null; + attemptCopy = attemptCopy || destinationElementEEType->BaseType == null; if (!attemptCopy) throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); @@ -348,16 +348,16 @@ private static unsafe void CopyImplGcRefArray(Array sourceArray, int sourceIndex // private static unsafe void CopyImplValueTypeArrayToReferenceArray(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { - Debug.Assert(sourceArray.ElementEEType.IsValueType); - Debug.Assert(!destinationArray.ElementEEType.IsValueType && !destinationArray.ElementEEType.IsPointer && !destinationArray.ElementEEType.IsFunctionPointer); + Debug.Assert(sourceArray.ElementMethodTable->IsValueType); + Debug.Assert(!destinationArray.ElementMethodTable->IsValueType && !destinationArray.ElementMethodTable->IsPointer && !destinationArray.ElementMethodTable->IsFunctionPointer); // Caller has already validated this. - Debug.Assert(RuntimeImports.AreTypesAssignable(sourceArray.ElementEEType, destinationArray.ElementEEType)); + Debug.Assert(RuntimeImports.AreTypesAssignable(sourceArray.ElementMethodTable, destinationArray.ElementMethodTable)); if (reliable) throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); - EETypePtr sourceElementEEType = sourceArray.ElementEEType; + MethodTable* sourceElementEEType = sourceArray.ElementMethodTable; nuint sourceElementSize = sourceArray.ElementSize; fixed (byte* pSourceArray = &MemoryMarshal.GetArrayDataReference(sourceArray)) @@ -378,15 +378,15 @@ private static unsafe void CopyImplValueTypeArrayToReferenceArray(Array sourceAr // private static unsafe void CopyImplReferenceArrayToValueTypeArray(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { - Debug.Assert(!sourceArray.ElementEEType.IsValueType && !sourceArray.ElementEEType.IsPointer && !sourceArray.ElementEEType.IsFunctionPointer); - Debug.Assert(destinationArray.ElementEEType.IsValueType); + Debug.Assert(!sourceArray.ElementMethodTable->IsValueType && !sourceArray.ElementMethodTable->IsPointer && !sourceArray.ElementMethodTable->IsFunctionPointer); + Debug.Assert(destinationArray.ElementMethodTable->IsValueType); if (reliable) throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); - EETypePtr destinationElementEEType = destinationArray.ElementEEType; + MethodTable* destinationElementEEType = destinationArray.ElementMethodTable; nuint destinationElementSize = destinationArray.ElementSize; - bool isNullable = destinationElementEEType.IsNullable; + bool isNullable = destinationElementEEType->IsNullable; fixed (byte* pDestinationArray = &MemoryMarshal.GetArrayDataReference(destinationArray)) { @@ -403,7 +403,7 @@ private static unsafe void CopyImplReferenceArrayToValueTypeArray(Array sourceAr } else { - EETypePtr eeType = boxedValue.GetEETypePtr(); + MethodTable* eeType = boxedValue.GetMethodTable(); if (!(RuntimeImports.AreTypesAssignable(eeType, destinationElementEEType))) throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); } @@ -420,10 +420,10 @@ private static unsafe void CopyImplReferenceArrayToValueTypeArray(Array sourceAr // private static unsafe void CopyImplValueTypeArrayWithInnerGcRefs(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { - Debug.Assert(sourceArray.GetEETypePtr() == destinationArray.GetEETypePtr()); - Debug.Assert(sourceArray.ElementEEType.IsValueType); + Debug.Assert(sourceArray.GetMethodTable() == destinationArray.GetMethodTable()); + Debug.Assert(sourceArray.ElementMethodTable->IsValueType); - EETypePtr sourceElementEEType = sourceArray.GetEETypePtr().ArrayElementType; + MethodTable* sourceElementEEType = sourceArray.GetMethodTable()->RelatedParameterType; bool reverseCopy = ((object)sourceArray == (object)destinationArray) && (sourceIndex < destinationIndex); // Copy scenario: ValueType-array to value-type array with embedded gc-refs. @@ -487,10 +487,10 @@ private static unsafe void CopyImplValueTypeArrayWithInnerGcRefs(Array sourceArr // private static unsafe void CopyImplValueTypeArrayNoInnerGcRefs(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { - Debug.Assert((sourceArray.ElementEEType.IsValueType && !sourceArray.ElementEEType.ContainsGCPointers) || - sourceArray.ElementEEType.IsPointer || sourceArray.ElementEEType.IsFunctionPointer); - Debug.Assert((destinationArray.ElementEEType.IsValueType && !destinationArray.ElementEEType.ContainsGCPointers) || - destinationArray.ElementEEType.IsPointer || destinationArray.ElementEEType.IsFunctionPointer); + Debug.Assert((sourceArray.ElementMethodTable->IsValueType && !sourceArray.ElementMethodTable->ContainsGCPointers) || + sourceArray.ElementMethodTable->IsPointer || sourceArray.ElementMethodTable->IsFunctionPointer); + Debug.Assert((destinationArray.ElementMethodTable->IsValueType && !destinationArray.ElementMethodTable->ContainsGCPointers) || + destinationArray.ElementMethodTable->IsPointer || destinationArray.ElementMethodTable->IsFunctionPointer); // Copy scenario: ValueType-array to value-type array with no embedded gc-refs. nuint elementSize = sourceArray.ElementSize; @@ -506,18 +506,18 @@ ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), ( // private static unsafe void CopyImplPrimitiveTypeWithWidening(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable) { - EETypePtr sourceElementEEType = sourceArray.ElementEEType; - EETypePtr destinationElementEEType = destinationArray.ElementEEType; + MethodTable* sourceElementEEType = sourceArray.ElementMethodTable; + MethodTable* destinationElementEEType = destinationArray.ElementMethodTable; - Debug.Assert(sourceElementEEType.IsPrimitive && destinationElementEEType.IsPrimitive); // Caller has already validated this. + Debug.Assert(sourceElementEEType->IsPrimitive && destinationElementEEType->IsPrimitive); // Caller has already validated this. - EETypeElementType sourceElementType = sourceElementEEType.ElementType; - EETypeElementType destElementType = destinationElementEEType.ElementType; + EETypeElementType sourceElementType = sourceElementEEType->ElementType; + EETypeElementType destElementType = destinationElementEEType->ElementType; nuint srcElementSize = sourceArray.ElementSize; nuint destElementSize = destinationArray.ElementSize; - if ((sourceElementEEType.IsEnum || destinationElementEEType.IsEnum) && sourceElementType != destElementType) + if ((sourceElementEEType->IsEnum || destinationElementEEType->IsEnum) && sourceElementType != destElementType) throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); if (reliable) @@ -847,19 +847,19 @@ public int GetLength(int dimension) return length; } - public int Rank + public unsafe int Rank { get { - return this.GetEETypePtr().ArrayRank; + return this.GetMethodTable()->ArrayRank; } } // Allocate new multidimensional array of given dimensions. Assumes that pLengths is immutable. - internal static unsafe Array NewMultiDimArray(EETypePtr eeType, int* pLengths, int rank) + internal static unsafe Array NewMultiDimArray(MethodTable* eeType, int* pLengths, int rank) { - Debug.Assert(eeType.IsArray && !eeType.IsSzArray); - Debug.Assert(rank == eeType.ArrayRank); + Debug.Assert(eeType->IsArray && !eeType->IsSzArray); + Debug.Assert(rank == eeType->ArrayRank); // Code below assumes 0 lower bounds. MdArray of rank 1 with zero lower bounds should never be allocated. // The runtime always allocates an SzArray for those: @@ -981,23 +981,23 @@ private unsafe nint GetFlattenedIndex(ReadOnlySpan indices) } } - internal object? InternalGetValue(nint flattenedIndex) + internal unsafe object? InternalGetValue(nint flattenedIndex) { Debug.Assert((nuint)flattenedIndex < NativeLength); - if (ElementEEType.IsPointer || ElementEEType.IsFunctionPointer) + if (ElementMethodTable->IsPointer || ElementMethodTable->IsFunctionPointer) throw new NotSupportedException(SR.NotSupported_Type); ref byte element = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(this), (nuint)flattenedIndex * ElementSize); - EETypePtr pElementEEType = ElementEEType; - if (pElementEEType.IsValueType) + MethodTable* pElementEEType = ElementMethodTable; + if (pElementEEType->IsValueType) { return RuntimeImports.RhBox(pElementEEType, ref element); } else { - Debug.Assert(!pElementEEType.IsPointer && !pElementEEType.IsFunctionPointer); + Debug.Assert(!pElementEEType->IsPointer && !pElementEEType->IsFunctionPointer); return Unsafe.As(ref element); } } @@ -1008,19 +1008,19 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex) ref byte element = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(this), (nuint)flattenedIndex * ElementSize); - EETypePtr pElementEEType = ElementEEType; - if (pElementEEType.IsValueType) + MethodTable* pElementEEType = ElementMethodTable; + if (pElementEEType->IsValueType) { // Unlike most callers of InvokeUtils.ChangeType(), Array.SetValue() does *not* permit conversion from a primitive to an Enum. - if (value != null && !(value.GetEETypePtr() == pElementEEType) && pElementEEType.IsEnum) + if (value != null && !(value.GetMethodTable() == pElementEEType) && pElementEEType->IsEnum) throw new InvalidCastException(SR.Format(SR.Arg_ObjObjEx, value.GetType(), Type.GetTypeFromHandle(new RuntimeTypeHandle(pElementEEType)))); value = InvokeUtils.CheckArgument(value, pElementEEType, InvokeUtils.CheckArgumentSemantics.ArraySet, binderBundle: null); - Debug.Assert(value == null || RuntimeImports.AreTypesAssignable(value.GetEETypePtr(), pElementEEType)); + Debug.Assert(value == null || RuntimeImports.AreTypesAssignable(value.GetMethodTable(), pElementEEType)); RuntimeImports.RhUnbox(value, ref element, pElementEEType); } - else if (pElementEEType.IsPointer || pElementEEType.IsFunctionPointer) + else if (pElementEEType->IsPointer || pElementEEType->IsFunctionPointer) { throw new NotSupportedException(SR.NotSupported_Type); } @@ -1038,22 +1038,16 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex) } } - internal EETypePtr ElementEEType - { - get - { - return this.GetEETypePtr().ArrayElementType; - } - } + internal unsafe MethodTable* ElementMethodTable => this.GetMethodTable()->RelatedParameterType; - internal CorElementType GetCorElementTypeOfElementType() + internal unsafe CorElementType GetCorElementTypeOfElementType() { - return ElementEEType.CorElementType; + return new EETypePtr(ElementMethodTable).CorElementType; } - internal bool IsValueOfElementType(object o) + internal unsafe bool IsValueOfElementType(object o) { - return ElementEEType.Equals(o.GetEETypePtr()); + return ElementMethodTable == o.GetMethodTable(); } // diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index a427896ac230c5..3aee8254f0fee6 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -11,6 +11,7 @@ using System.Text; using Internal.Reflection.Augments; +using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; @@ -112,7 +113,7 @@ internal unsafe IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParam else { if (m_firstParameter != null) - typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(m_firstParameter.GetEETypePtr()); + typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(m_firstParameter.GetMethodTable()); // TODO! Implementation issue for generic invokes here ... we need another IntPtr for uniqueness. @@ -348,20 +349,20 @@ internal bool IsOpenStatic } } - internal static bool InternalEqualTypes(object a, object b) + internal static unsafe bool InternalEqualTypes(object a, object b) { - return a.GetEETypePtr() == b.GetEETypePtr(); + return a.GetMethodTable() == b.GetMethodTable(); } // Returns a new delegate of the specified type whose implementation is provided by the // provided delegate. - internal static Delegate CreateObjectArrayDelegate(Type t, Func handler) + internal static unsafe Delegate CreateObjectArrayDelegate(Type t, Func handler) { RuntimeTypeHandle typeHandle = t.TypeHandle; - EETypePtr delegateEEType = typeHandle.ToEETypePtr(); - Debug.Assert(!delegateEEType.IsNull); - Debug.Assert(delegateEEType.IsCanonical); + MethodTable* delegateEEType = typeHandle.ToMethodTable(); + Debug.Assert(delegateEEType != null); + Debug.Assert(delegateEEType->IsCanonical); Delegate del = (Delegate)(RuntimeImports.RhNewObject(delegateEEType)); @@ -383,9 +384,9 @@ internal static Delegate CreateObjectArrayDelegate(Type t, Func + internal unsafe struct EETypePtr { private MethodTable* _value; @@ -39,308 +35,6 @@ internal EETypePtr(MethodTable* value) _value = value; } - internal MethodTable* ToPointer() - { - return _value; - } - - public override bool Equals(object? obj) - { - if (obj is EETypePtr) - { - return this == (EETypePtr)obj; - } - return false; - } - - public bool Equals(EETypePtr p) - { - return this == p; - } - - public static bool operator ==(EETypePtr value1, EETypePtr value2) - { - return value1._value == value2._value; - } - - public static bool operator !=(EETypePtr value1, EETypePtr value2) - { - return !(value1 == value2); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return (int)_value->HashCode; - } - - // Caution: You cannot safely compare RawValue's as RH does NOT unify EETypes. Use the == or Equals() methods exposed by EETypePtr itself. - internal IntPtr RawValue - { - get - { - return (IntPtr)_value; - } - } - - internal bool IsNull - { - get - { - return _value == null; - } - } - - internal bool IsArray - { - get - { - return _value->IsArray; - } - } - - internal bool IsSzArray - { - get - { - return _value->IsSzArray; - } - } - - internal bool IsPointer - { - get - { - return _value->IsPointer; - } - } - - internal bool IsFunctionPointer - { - get - { - return _value->IsFunctionPointer; - } - } - - internal bool IsByRef - { - get - { - return _value->IsByRef; - } - } - - internal bool IsValueType - { - get - { - return _value->IsValueType; - } - } - - internal bool IsString - { - get - { - return _value->IsString; - } - } - - // Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums. - internal bool IsPrimitive - { - get - { - return _value->IsPrimitive; - } - } - - internal bool IsEnum - { - get - { - return _value->IsEnum; - } - } - - // Gets a value indicating whether this is a generic type definition (an uninstantiated generic type). - internal bool IsGenericTypeDefinition - { - get - { - return _value->IsGenericTypeDefinition; - } - } - - // Gets a value indicating whether this is an instantiated generic type. - internal bool IsGeneric - { - get - { - return _value->IsGeneric; - } - } - - internal GenericArgumentCollection Instantiation - { - get - { - return new GenericArgumentCollection(_value->GenericArity, _value->GenericArguments); - } - } - - internal EETypePtr GenericDefinition - { - get - { - return new EETypePtr(_value->GenericDefinition); - } - } - - /// - /// Gets a value indicating whether this is an class, a struct, an enum, or an interface, - /// that is not generic type definition - /// - internal bool IsCanonical - { - get - { - return _value->IsCanonical; - } - } - - /// - /// Gets a value indicating whether this is a class, a struct, an enum, or an interface. - /// - internal bool IsDefType - { - get - { - return _value->IsDefType; - } - } - - internal bool IsDynamicType - { - get - { - return _value->IsDynamicType; - } - } - - internal bool IsInterface - { - get - { - return _value->IsInterface; - } - } - - internal bool IsByRefLike - { - get - { - return _value->IsByRefLike; - } - } - - internal bool IsNullable - { - get - { - return _value->IsNullable; - } - } - - internal bool HasCctor - { - get - { - return _value->HasCctor; - } - } - - internal bool IsTrackedReferenceWithFinalizer - { - get - { - return _value->IsTrackedReferenceWithFinalizer; - } - } - - internal EETypePtr NullableType - { - get - { - return new EETypePtr(_value->NullableType); - } - } - - internal EETypePtr ArrayElementType - { - get - { - return new EETypePtr(_value->RelatedParameterType); - } - } - - internal int ArrayRank - { - get - { - return _value->ArrayRank; - } - } - - internal InterfaceCollection Interfaces - { - get - { - return new InterfaceCollection(_value); - } - } - - internal EETypePtr BaseType - { - get - { - if (IsArray) - return EETypePtr.EETypePtrOf(); - - if (IsPointer || IsByRef || IsFunctionPointer) - return new EETypePtr(default(IntPtr)); - - EETypePtr baseEEType = new EETypePtr(_value->NonArrayBaseType); - return baseEEType; - } - } - - internal IntPtr DispatchMap - { - get - { - return (IntPtr)_value->DispatchMap; - } - } - - // Instance contains pointers to managed objects. - internal bool ContainsGCPointers - { - get - { - return _value->ContainsGCPointers; - } - } - - internal uint ValueTypeSize - { - get - { - return _value->ValueTypeSize; - } - } - internal CorElementType CorElementType { get @@ -389,7 +83,7 @@ internal CorElementType CorElementType } } - internal EETypeElementType ElementType + private EETypeElementType ElementType { get { @@ -404,70 +98,5 @@ internal RuntimeImports.RhCorElementTypeInfo CorElementTypeInfo return RuntimeImports.GetRhCorElementTypeInfo(CorElementType); } } - - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static EETypePtr EETypePtrOf() - { - // Compilers are required to provide a low level implementation of this method. - throw new NotImplementedException(); - } - - public struct InterfaceCollection - { - private MethodTable* _value; - - internal InterfaceCollection(MethodTable* value) - { - _value = value; - } - - public int Count - { - get - { - return _value->NumInterfaces; - } - } - - public EETypePtr this[int index] - { - get - { - Debug.Assert((uint)index < _value->NumInterfaces); - - return new EETypePtr(_value->InterfaceMap[index]); - } - } - } - - public struct GenericArgumentCollection - { - private MethodTableList _arguments; - private uint _argumentCount; - - internal GenericArgumentCollection(uint argumentCount, MethodTableList arguments) - { - _argumentCount = argumentCount; - _arguments = arguments; - } - - public int Length - { - get - { - return (int)_argumentCount; - } - } - - public EETypePtr this[int index] - { - get - { - Debug.Assert((uint)index < _argumentCount); - return new EETypePtr(_arguments[index]); - } - } - } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs index 50eca49d0a023c..baf2e424546e4e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Enum.NativeAot.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using Internal.Reflection.Augments; +using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.CompilerServices; @@ -21,12 +22,12 @@ namespace System public abstract partial class Enum : ValueType, IComparable, IFormattable, IConvertible { #pragma warning disable IDE0060 - internal static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) + internal static unsafe EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true) { Debug.Assert(enumType != null); Debug.Assert(enumType.IsEnum); - return enumType.TypeHandle.ToEETypePtr().ElementType switch + return enumType.TypeHandle.ToMethodTable()->ElementType switch { EETypeElementType.SByte or EETypeElementType.Byte => GetEnumInfo(enumType), EETypeElementType.Int16 or EETypeElementType.UInt16 => GetEnumInfo(enumType), @@ -61,20 +62,20 @@ internal static EnumInfo GetEnumInfo(RuntimeType enumType, b } #pragma warning restore - private static object InternalBoxEnum(Type enumType, long value) + private static unsafe object InternalBoxEnum(Type enumType, long value) { - return ToObject(enumType.TypeHandle.ToEETypePtr(), value); + return ToObject(enumType.TypeHandle.ToMethodTable(), value); } - private static CorElementType InternalGetCorElementType(RuntimeType rt) + private static unsafe CorElementType InternalGetCorElementType(RuntimeType rt) { Debug.Assert(rt.IsActualEnum); - return rt.TypeHandle.ToEETypePtr().CorElementType; + return new EETypePtr(rt.TypeHandle.ToMethodTable()).CorElementType; } - private CorElementType InternalGetCorElementType() + private unsafe CorElementType InternalGetCorElementType() { - return this.GetEETypePtr().CorElementType; + return new EETypePtr(this.GetMethodTable()).CorElementType; } // @@ -86,16 +87,16 @@ private CorElementType InternalGetCorElementType() // // The return value is "bool" if "value" is not an enum or an "integer type" as defined by the BCL Enum apis. // - internal static bool TryGetUnboxedValueOfEnumOrInteger(object value, out ulong result) + internal static unsafe bool TryGetUnboxedValueOfEnumOrInteger(object value, out ulong result) { - EETypePtr eeType = value.GetEETypePtr(); + MethodTable* eeType = value.GetMethodTable(); // For now, this check is required to flush out pointers. - if (!eeType.IsDefType) + if (!eeType->IsDefType) { result = 0; return false; } - EETypeElementType elementType = eeType.ElementType; + EETypeElementType elementType = eeType->ElementType; ref byte pValue = ref value.GetRawData(); @@ -152,12 +153,12 @@ internal static Type InternalGetUnderlyingType(RuntimeType enumType) } [Conditional("BIGENDIAN")] - private static unsafe void AdjustForEndianness(ref byte* pValue, EETypePtr enumEEType) + private static unsafe void AdjustForEndianness(ref byte* pValue, MethodTable* enumEEType) { // On Debug builds, include the big-endian code to help deter bitrot (the "Conditional("BIGENDIAN")" will prevent it from executing on little-endian). // On Release builds, exclude code to deter IL bloat and toolchain work. #if BIGENDIAN || DEBUG - EETypeElementType elementType = enumEEType.ElementType; + EETypeElementType elementType = enumEEType->ElementType; switch (elementType) { case EETypeElementType.SByte: @@ -187,9 +188,9 @@ private static unsafe void AdjustForEndianness(ref byte* pValue, EETypePtr enumE #region ToObject - internal static unsafe object ToObject(EETypePtr enumEEType, long value) + internal static unsafe object ToObject(MethodTable* enumEEType, long value) { - Debug.Assert(enumEEType.IsEnum); + Debug.Assert(enumEEType->IsEnum); byte* pValue = (byte*)&value; AdjustForEndianness(ref pValue, enumEEType); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs index 538fb9b7e246eb..bc61b34c3b3027 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs @@ -6,9 +6,11 @@ using System.Runtime; using System.Runtime.CompilerServices; +using MethodTable = Internal.Runtime.MethodTable; + namespace System { - internal static class InvokeUtils + internal static unsafe class InvokeUtils { // // Various reflection scenarios (Array.SetValue(), reflection Invoke, delegate DynamicInvoke and FieldInfo.Set()) perform @@ -37,22 +39,22 @@ internal enum CheckArgumentSemantics SetFieldDirect, // Throws ArgumentException - other than that, like DynamicInvoke except that enums and integers cannot be intermingled, and null cannot substitute for default(valuetype). } - internal static object? CheckArgument(object? srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle) + internal static object? CheckArgument(object? srcObject, MethodTable* dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle) { // Methods with ByRefLike types in signatures should be filtered out earlier - Debug.Assert(!dstEEType.IsByRefLike); + Debug.Assert(!dstEEType->IsByRefLike); if (srcObject == null) { // null -> default(T) - if (dstEEType.IsPointer) + if (dstEEType->IsPointer) { return default(IntPtr); } - else if (dstEEType.IsValueType && !dstEEType.IsNullable) + else if (dstEEType->IsValueType && !dstEEType->IsNullable) { if (semantics == CheckArgumentSemantics.SetFieldDirect) - throw CreateChangeTypeException(typeof(object).TypeHandle.ToEETypePtr(), dstEEType, semantics); + throw CreateChangeTypeException(MethodTable.Of(), dstEEType, semantics); return Runtime.RuntimeImports.RhNewObject(dstEEType); } else @@ -62,11 +64,11 @@ internal enum CheckArgumentSemantics } else { - EETypePtr srcEEType = srcObject.GetEETypePtr(); + MethodTable* srcEEType = srcObject.GetMethodTable(); - if (srcEEType.RawValue == dstEEType.RawValue || + if (srcEEType == dstEEType || RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) || - (dstEEType.IsInterface && srcObject is Runtime.InteropServices.IDynamicInterfaceCastable castable + (dstEEType->IsInterface && srcObject is Runtime.InteropServices.IDynamicInterfaceCastable castable && castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false))) { return srcObject; @@ -76,7 +78,7 @@ internal enum CheckArgumentSemantics } } - internal static object? CheckArgumentConversions(object srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle) + internal static object? CheckArgumentConversions(object srcObject, MethodTable* dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle) { object? dstObject; Exception exception = ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(srcObject, dstEEType, semantics, out dstObject); @@ -96,17 +98,17 @@ internal enum CheckArgumentSemantics } // Special coersion rules for primitives, enums and pointer. - private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(object srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, out object? dstObject) + private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(object srcObject, MethodTable* dstEEType, CheckArgumentSemantics semantics, out object? dstObject) { - EETypePtr srcEEType = srcObject.GetEETypePtr(); + MethodTable* srcEEType = srcObject.GetMethodTable(); - if (semantics == CheckArgumentSemantics.SetFieldDirect && (srcEEType.IsEnum || dstEEType.IsEnum)) + if (semantics == CheckArgumentSemantics.SetFieldDirect && (srcEEType->IsEnum || dstEEType->IsEnum)) { dstObject = null; return CreateChangeTypeException(srcEEType, dstEEType, semantics); } - if (dstEEType.IsPointer || dstEEType.IsFunctionPointer) + if (dstEEType->IsPointer || dstEEType->IsFunctionPointer) { Exception exception = ConvertPointerIfPossible(srcObject, dstEEType, semantics, out object dstPtr); if (exception != null) @@ -118,14 +120,14 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje return null; } - if (!(srcEEType.IsPrimitive && dstEEType.IsPrimitive)) + if (!(srcEEType->IsPrimitive && dstEEType->IsPrimitive)) { dstObject = null; return CreateChangeTypeException(srcEEType, dstEEType, semantics); } - CorElementType dstCorElementType = dstEEType.CorElementType; - if (!srcEEType.CorElementTypeInfo.CanWidenTo(dstCorElementType)) + CorElementType dstCorElementType = new EETypePtr(dstEEType).CorElementType; + if (!new EETypePtr(srcEEType).CorElementTypeInfo.CanWidenTo(dstCorElementType)) { dstObject = null; return CreateChangeTypeArgumentException(srcEEType, dstEEType); @@ -139,51 +141,51 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje case CorElementType.ELEMENT_TYPE_CHAR: char charValue = Convert.ToChar(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, charValue) : charValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, charValue) : charValue; break; case CorElementType.ELEMENT_TYPE_I1: sbyte sbyteValue = Convert.ToSByte(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, sbyteValue) : sbyteValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, sbyteValue) : sbyteValue; break; case CorElementType.ELEMENT_TYPE_I2: short shortValue = Convert.ToInt16(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, shortValue) : shortValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, shortValue) : shortValue; break; case CorElementType.ELEMENT_TYPE_I4: int intValue = Convert.ToInt32(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, intValue) : intValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, intValue) : intValue; break; case CorElementType.ELEMENT_TYPE_I8: long longValue = Convert.ToInt64(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, longValue) : longValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, longValue) : longValue; break; case CorElementType.ELEMENT_TYPE_U1: byte byteValue = Convert.ToByte(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, byteValue) : byteValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, byteValue) : byteValue; break; case CorElementType.ELEMENT_TYPE_U2: ushort ushortValue = Convert.ToUInt16(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, ushortValue) : ushortValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, ushortValue) : ushortValue; break; case CorElementType.ELEMENT_TYPE_U4: uint uintValue = Convert.ToUInt32(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, uintValue) : uintValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, uintValue) : uintValue; break; case CorElementType.ELEMENT_TYPE_U8: ulong ulongValue = Convert.ToUInt64(srcObject); - dstObject = dstEEType.IsEnum ? Enum.ToObject(dstEEType, (long)ulongValue) : ulongValue; + dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, (long)ulongValue) : ulongValue; break; case CorElementType.ELEMENT_TYPE_R4: - if (srcEEType.CorElementType == CorElementType.ELEMENT_TYPE_CHAR) + if (new EETypePtr(srcEEType).CorElementType == CorElementType.ELEMENT_TYPE_CHAR) { dstObject = (float)(char)srcObject; } @@ -194,7 +196,7 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje break; case CorElementType.ELEMENT_TYPE_R8: - if (srcEEType.CorElementType == CorElementType.ELEMENT_TYPE_CHAR) + if (new EETypePtr(srcEEType).CorElementType == CorElementType.ELEMENT_TYPE_CHAR) { dstObject = (double)(char)srcObject; } @@ -210,11 +212,11 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje return CreateChangeTypeException(srcEEType, dstEEType, semantics); } - Debug.Assert(dstObject.GetEETypePtr() == dstEEType); + Debug.Assert(dstObject.GetMethodTable() == dstEEType); return null; } - private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, out object dstPtr) + private static Exception ConvertPointerIfPossible(object srcObject, MethodTable* dstEEType, CheckArgumentSemantics semantics, out object dstPtr) { if (srcObject is IntPtr or UIntPtr) { @@ -224,7 +226,7 @@ private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr ds if (srcObject is Pointer srcPointer) { - if (dstEEType == typeof(void*).TypeHandle.ToEETypePtr() || RuntimeImports.AreTypesAssignable(pSourceType: srcPointer.GetPointerType().TypeHandle.ToEETypePtr(), pTargetType: dstEEType)) + if (dstEEType == typeof(void*).TypeHandle.ToMethodTable() || RuntimeImports.AreTypesAssignable(pSourceType: srcPointer.GetPointerType().TypeHandle.ToMethodTable(), pTargetType: dstEEType)) { dstPtr = srcPointer.GetPointerValue(); return null; @@ -232,10 +234,10 @@ private static Exception ConvertPointerIfPossible(object srcObject, EETypePtr ds } dstPtr = null; - return CreateChangeTypeException(srcObject.GetEETypePtr(), dstEEType, semantics); + return CreateChangeTypeException(srcObject.GetMethodTable(), dstEEType, semantics); } - private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePtr dstEEType, CheckArgumentSemantics semantics) + private static Exception CreateChangeTypeException(MethodTable* srcEEType, MethodTable* dstEEType, CheckArgumentSemantics semantics) { switch (semantics) { @@ -250,10 +252,10 @@ private static Exception CreateChangeTypeException(EETypePtr srcEEType, EETypePt } } - internal static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, EETypePtr dstEEType, bool destinationIsByRef = false) + internal static ArgumentException CreateChangeTypeArgumentException(MethodTable* srcEEType, MethodTable* dstEEType, bool destinationIsByRef = false) => CreateChangeTypeArgumentException(srcEEType, Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType)), destinationIsByRef); - internal static ArgumentException CreateChangeTypeArgumentException(EETypePtr srcEEType, Type dstType, bool destinationIsByRef = false) + internal static ArgumentException CreateChangeTypeArgumentException(MethodTable* srcEEType, Type dstType, bool destinationIsByRef = false) { object? destinationTypeName = dstType; if (destinationIsByRef) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs index 057db98ff670a9..98d2565a07ee2b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs @@ -138,10 +138,10 @@ public sealed override int GetHashCode() return ReferenceEquals(d2, d1) ? false : !d2.Equals(d1); } - private MulticastDelegate NewMulticastDelegate(Delegate[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false) + private unsafe MulticastDelegate NewMulticastDelegate(Delegate[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false) { // First, allocate a new multicast delegate just like this one, i.e. same type as the this object - MulticastDelegate result = (MulticastDelegate)RuntimeImports.RhNewObject(this.GetEETypePtr()); + MulticastDelegate result = (MulticastDelegate)RuntimeImports.RhNewObject(this.GetMethodTable()); // Performance optimization - if this already points to a true multicast delegate, // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.NativeAot.cs index 1d68cad28ab306..727fbc9fbfdd2b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Object.NativeAot.cs @@ -26,11 +26,11 @@ public Type GetType() } [Intrinsic] - protected internal object MemberwiseClone() + protected internal unsafe object MemberwiseClone() { - object clone = this.GetEETypePtr().IsArray ? - RuntimeImports.RhNewArray(this.GetEETypePtr(), Unsafe.As(this).Length) : - RuntimeImports.RhNewObject(this.GetEETypePtr()); + object clone = this.GetMethodTable()->IsArray ? + RuntimeImports.RhNewArray(this.GetMethodTable(), Unsafe.As(this).Length) : + RuntimeImports.RhNewObject(this.GetMethodTable()); // copy contents of "this" to the clone @@ -38,7 +38,7 @@ protected internal object MemberwiseClone() ref byte src = ref this.GetRawData(); ref byte dst = ref clone.GetRawData(); - if (this.GetEETypePtr().ContainsGCPointers) + if (this.GetMethodTable()->ContainsGCPointers) Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount); else Buffer.Memmove(ref dst, ref src, byteCount); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs index 4f2e51afe59da5..012e410885b8ea 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs @@ -7,10 +7,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Internal.Runtime; + namespace System.Reflection { // caches information required for efficient argument validation and type coercion for reflection Invoke. - public class DynamicInvokeInfo + public unsafe class DynamicInvokeInfo { // Public state public MethodBase Method { get; } @@ -22,7 +24,7 @@ public class DynamicInvokeInfo // private readonly bool _isValueTypeInstanceMethod; private readonly bool _needsCopyBack; private readonly Transform _returnTransform; - private readonly EETypePtr _returnType; + private readonly MethodTable* _returnType; private readonly ArgumentInfo[] _arguments; // We use negative argument count to signal unsupported invoke signatures @@ -40,19 +42,19 @@ private enum Transform AllocateReturnBox = 0x0020, } - private readonly struct ArgumentInfo + private readonly unsafe struct ArgumentInfo { - internal ArgumentInfo(Transform transform, EETypePtr type) + internal ArgumentInfo(Transform transform, MethodTable* type) { Transform = transform; Type = type; } internal Transform Transform { get; } - internal EETypePtr Type { get; } + internal MethodTable* Type { get; } } - public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) + public unsafe DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) { Method = method; InvokeThunk = invokeThunk; @@ -83,26 +85,26 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) // This can return a null MethodTable for reference types. // The compiler makes sure it returns a non-null MT for everything else. - EETypePtr eeArgumentType = argumentType.ToEETypePtrMayBeNull(); + MethodTable* eeArgumentType = argumentType.ToMethodTableMayBeNull(); if (argumentType.IsValueType) { - Debug.Assert(eeArgumentType.IsValueType); + Debug.Assert(eeArgumentType->IsValueType); - if (eeArgumentType.IsByRefLike) + if (eeArgumentType->IsByRefLike) _argumentCount = ArgumentCount_NotSupported_ByRefLike; - if (eeArgumentType.IsNullable) + if (eeArgumentType->IsNullable) transform |= Transform.Nullable; } else if (argumentType.IsPointer) { - Debug.Assert(eeArgumentType.IsPointer); + Debug.Assert(eeArgumentType->IsPointer); transform |= Transform.Pointer; } else if (argumentType.IsFunctionPointer) { - Debug.Assert(eeArgumentType.IsFunctionPointer); + Debug.Assert(eeArgumentType->IsFunctionPointer); transform |= Transform.FunctionPointer; } @@ -128,20 +130,20 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) } Debug.Assert(!returnType.IsByRef); - EETypePtr eeReturnType = returnType.ToEETypePtrMayBeNull(); + MethodTable* eeReturnType = returnType.ToMethodTableMayBeNull(); if (returnType.IsValueType) { - Debug.Assert(eeReturnType.IsValueType); + Debug.Assert(eeReturnType->IsValueType); if (returnType != typeof(void)) { - if (eeReturnType.IsByRefLike) + if (eeReturnType->IsByRefLike) _argumentCount = ArgumentCount_NotSupported_ByRefLike; if ((transform & Transform.ByRef) == 0) transform |= Transform.AllocateReturnBox; - if (eeReturnType.IsNullable) + if (eeReturnType->IsNullable) transform |= Transform.Nullable; } else @@ -152,7 +154,7 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) } else if (returnType.IsPointer) { - Debug.Assert(eeReturnType.IsPointer); + Debug.Assert(eeReturnType->IsPointer); transform |= Transform.Pointer; if ((transform & Transform.ByRef) == 0) @@ -160,7 +162,7 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) } else if (returnType.IsFunctionPointer) { - Debug.Assert(eeReturnType.IsFunctionPointer); + Debug.Assert(eeReturnType->IsFunctionPointer); transform |= Transform.FunctionPointer; if ((transform & Transform.ByRef) == 0) @@ -217,7 +219,7 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) { returnObject = RuntimeImports.RhNewObject( (_returnTransform & (Transform.Pointer | Transform.FunctionPointer)) != 0 ? - EETypePtr.EETypePtrOf() : _returnType); + MethodTable.Of() : _returnType); ret = ref returnObject.GetRawData(); } @@ -285,7 +287,7 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) { returnObject = RuntimeImports.RhNewObject( (_returnTransform & (Transform.Pointer | Transform.FunctionPointer)) != 0 ? - EETypePtr.EETypePtrOf() : _returnType); + MethodTable.Of() : _returnType); ret = ref returnObject.GetRawData(); } @@ -351,7 +353,7 @@ public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) { returnObject = RuntimeImports.RhNewObject( (_returnTransform & (Transform.Pointer | Transform.FunctionPointer)) != 0 ? - EETypePtr.EETypePtrOf() : _returnType); + MethodTable.Of() : _returnType); ret = ref returnObject.GetRawData(); } @@ -587,17 +589,17 @@ private unsafe ref byte InvokeDirectWithFewArguments( { // In case if the parameter is nullable Enum type the ParameterInfo.DefaultValue returns a raw value which // needs to be parsed to the Enum type, for more info: https://github.com/dotnet/runtime/issues/12924 - EETypePtr nullableType = argumentInfo.Type.NullableType; - if (nullableType.IsEnum) + MethodTable* nullableType = argumentInfo.Type->NullableType; + if (nullableType->IsEnum) { - defaultValue = Enum.ToObject(Type.GetTypeFromMethodTable(nullableType.ToPointer()), defaultValue); + defaultValue = Enum.ToObject(Type.GetTypeFromMethodTable(nullableType), defaultValue); } } return defaultValue; } - private void ThrowForNeverValidNonNullArgument(EETypePtr srcEEType, int index) + private void ThrowForNeverValidNonNullArgument(MethodTable* srcEEType, int index) { Debug.Assert(index != 0 || _isStatic); throw InvokeUtils.CreateChangeTypeArgumentException(srcEEType, Method.GetParametersAsSpan()[index - (_isStatic ? 0 : 1)].ParameterType, destinationIsByRef: false); @@ -622,7 +624,7 @@ private unsafe void CheckArguments( if ((argumentInfo.Transform & Transform.Reference) == 0) arg = RuntimeImports.RhNewObject( (argumentInfo.Transform & (Transform.Pointer | Transform.FunctionPointer)) != 0 ? - EETypePtr.EETypePtrOf() : argumentInfo.Type); + MethodTable.Of() : argumentInfo.Type); } else { @@ -639,20 +641,20 @@ private unsafe void CheckArguments( goto Again; // Redo the argument handling to deal with null } - EETypePtr srcEEType = arg.GetEETypePtr(); - EETypePtr dstEEType = argumentInfo.Type; + MethodTable* srcEEType = arg.GetMethodTable(); + MethodTable* dstEEType = argumentInfo.Type; - if (srcEEType.RawValue != dstEEType.RawValue) + if (srcEEType != dstEEType) { // Destination type can be null if we don't have a MethodTable for this type. This means one cannot // possibly pass a valid non-null object instance here. - if (dstEEType.IsNull) + if (dstEEType == null) { ThrowForNeverValidNonNullArgument(srcEEType, i); } if (!(RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) || - (dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable + (dstEEType->IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable && castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false)))) { // ByRefs have to be exact match @@ -712,24 +714,24 @@ private unsafe void CheckArguments( if ((argumentInfo.Transform & Transform.Reference) == 0) arg = RuntimeImports.RhNewObject( (argumentInfo.Transform & (Transform.Pointer | Transform.FunctionPointer)) != 0 ? - EETypePtr.EETypePtrOf() : argumentInfo.Type); + MethodTable.Of() : argumentInfo.Type); } else { - EETypePtr srcEEType = arg.GetEETypePtr(); - EETypePtr dstEEType = argumentInfo.Type; + MethodTable* srcEEType = arg.GetMethodTable(); + MethodTable* dstEEType = argumentInfo.Type; - if (srcEEType.RawValue != dstEEType.RawValue) + if (srcEEType != dstEEType) { // Destination type can be null if we don't have a MethodTable for this type. This means one cannot // possibly pass a valid non-null object instance here. - if (dstEEType.IsNull) + if (dstEEType == null) { ThrowForNeverValidNonNullArgument(srcEEType, i); } if (!(RuntimeImports.AreTypesAssignable(srcEEType, dstEEType) || - (dstEEType.IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable + (dstEEType->IsInterface && arg is System.Runtime.InteropServices.IDynamicInterfaceCastable castable && castable.IsInterfaceImplemented(new RuntimeTypeHandle(dstEEType), throwIfNotImplemented: false)))) { // ByRefs have to be exact match @@ -789,14 +791,14 @@ private unsafe void CopyBackToArray(ref object? src, object?[] dest) { if ((transform & Transform.Pointer) != 0) { - Type type = Type.GetTypeFromMethodTable(argumentInfo.Type.ToPointer()); + Type type = Type.GetTypeFromMethodTable(argumentInfo.Type); Debug.Assert(type.IsPointer); obj = Pointer.Box((void*)Unsafe.As(ref obj.GetRawData()), type); } else { obj = RuntimeImports.RhBox( - (transform & Transform.FunctionPointer) != 0 ? EETypePtr.EETypePtrOf() : argumentInfo.Type, + (transform & Transform.FunctionPointer) != 0 ? MethodTable.Of() : argumentInfo.Type, ref obj.GetRawData()); } } @@ -824,14 +826,14 @@ private unsafe void CopyBackToSpan(Span src, Span dest) { if ((transform & Transform.Pointer) != 0) { - Type type = Type.GetTypeFromMethodTable(argumentInfo.Type.ToPointer()); + Type type = Type.GetTypeFromMethodTable(argumentInfo.Type); Debug.Assert(type.IsPointer); obj = Pointer.Box((void*)Unsafe.As(ref obj.GetRawData()), type); } else { obj = RuntimeImports.RhBox( - (transform & Transform.FunctionPointer) != 0 ? EETypePtr.EETypePtrOf() : argumentInfo.Type, + (transform & Transform.FunctionPointer) != 0 ? MethodTable.Of() : argumentInfo.Type, ref obj.GetRawData()); } } @@ -854,14 +856,14 @@ private unsafe object ReturnTransform(ref byte byref, bool wrapInTargetInvocatio object obj; if ((_returnTransform & Transform.Pointer) != 0) { - Type type = Type.GetTypeFromMethodTable(_returnType.ToPointer()); + Type type = Type.GetTypeFromMethodTable(_returnType); Debug.Assert(type.IsPointer); obj = Pointer.Box((void*)Unsafe.As(ref byref), type); } else if ((_returnTransform & Transform.FunctionPointer) != 0) { - Debug.Assert(Type.GetTypeFromMethodTable(_returnType.ToPointer()).IsFunctionPointer); - obj = RuntimeImports.RhBox(EETypePtr.EETypePtrOf(), ref byref); + Debug.Assert(Type.GetTypeFromMethodTable(_returnType).IsFunctionPointer); + obj = RuntimeImports.RhBox(MethodTable.Of(), ref byref); } else if ((_returnTransform & Transform.Reference) != 0) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs index 33423374354192..262ddb9857b75f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs @@ -59,19 +59,19 @@ public static void RunModuleConstructor(ModuleHandle module) } [return: NotNullIfNotNull(nameof(obj))] - public static object? GetObjectValue(object? obj) + public static unsafe object? GetObjectValue(object? obj) { if (obj == null) return null; - EETypePtr eeType = obj.GetEETypePtr(); - if ((!eeType.IsValueType) || eeType.IsPrimitive) + MethodTable* eeType = obj.GetMethodTable(); + if ((!eeType->IsValueType) || eeType->IsPrimitive) return obj; return obj.MemberwiseClone(); } - public static new bool Equals(object? o1, object? o2) + public static new unsafe bool Equals(object? o1, object? o2) { if (o1 == o2) return true; @@ -80,11 +80,11 @@ public static void RunModuleConstructor(ModuleHandle module) return false; // If it's not a value class, don't compare by value - if (!o1.GetEETypePtr().IsValueType) + if (!o1.GetMethodTable()->IsValueType) return false; // Make sure they are the same type. - if (o1.GetEETypePtr() != o2.GetEETypePtr()) + if (o1.GetMethodTable() != o2.GetMethodTable()) return false; return RuntimeImports.RhCompareObjectContentsAndPadding(o1, o2); @@ -182,17 +182,16 @@ public static unsafe bool TryEnsureSufficientExecutionStack() } [Intrinsic] - public static bool IsReferenceOrContainsReferences() + public static unsafe bool IsReferenceOrContainsReferences() { - var pEEType = EETypePtr.EETypePtrOf(); - return !pEEType.IsValueType || pEEType.ContainsGCPointers; + MethodTable* pEEType = MethodTable.Of(); + return !pEEType->IsValueType || pEEType->ContainsGCPointers; } [Intrinsic] - internal static bool IsReference() + internal static unsafe bool IsReference() { - var pEEType = EETypePtr.EETypePtrOf(); - return !pEEType.IsValueType; + return !MethodTable.Of()->IsValueType; } [Intrinsic] @@ -232,9 +231,6 @@ internal static unsafe ushort GetElementSize(this Array array) internal static unsafe ref MethodTable* GetMethodTableRef(this object obj) => ref obj.m_pEEType; - internal static unsafe EETypePtr GetEETypePtr(this object obj) - => new EETypePtr(obj.m_pEEType); - // Returns true iff the object has a component size; // i.e., is variable length like System.String or Array. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -350,7 +346,7 @@ public static unsafe object GetUninitializedObject( throw ReflectionCoreExecution.ExecutionEnvironment.CreateMissingMetadataException(type); } // Paranoid check: not-meant-for-GC-heap types should be reliably identifiable by empty vtable. - Debug.Assert(!mt->ContainsGCPointers || RuntimeImports.RhGetGCDescSize(new EETypePtr(mt)) != 0); + Debug.Assert(!mt->ContainsGCPointers || RuntimeImports.RhGetGCDescSize(mt) != 0); if (mt->IsNullable) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs index 4d404018b0a079..4d156c039422ae 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs @@ -9,6 +9,8 @@ using System.Runtime.Versioning; using System.Threading; +using Internal.Runtime; + namespace System.Runtime.InteropServices { /// @@ -417,7 +419,7 @@ internal sealed unsafe class ManagedObjectWrapperHolder static ManagedObjectWrapperHolder() { delegate* unmanaged callback = &IsRootedCallback; - if (!RuntimeImports.RhRegisterRefCountedHandleCallback((nint)callback, EETypePtr.EETypePtrOf())) + if (!RuntimeImports.RhRegisterRefCountedHandleCallback((nint)callback, MethodTable.Of())) { throw new OutOfMemoryException(); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs index a371386010ceeb..2b03b2b92806d6 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/InteropExtensions.cs @@ -15,37 +15,30 @@ namespace System.Runtime.InteropServices /// /// Hooks for interop code to access internal functionality in System.Private.CoreLib.dll. /// - internal static class InteropExtensions + internal static unsafe class InteropExtensions { - internal static bool MightBeBlittable(this EETypePtr eeType) + internal static bool MightBeBlittable(this RuntimeTypeHandle handle) { // // This is used as the approximate implementation of MethodTable::IsBlittable(). It will err in the direction of declaring // things blittable since it is used for argument validation only. // - return !eeType.ContainsGCPointers; + return !handle.ToMethodTable()->ContainsGCPointers; } public static bool IsBlittable(this RuntimeTypeHandle handle) { - return handle.ToEETypePtr().MightBeBlittable(); - } - - public static bool IsBlittable(this object obj) - { - return obj.GetEETypePtr().MightBeBlittable(); + return handle.MightBeBlittable(); } public static bool IsGenericType(this RuntimeTypeHandle handle) { - EETypePtr eeType = handle.ToEETypePtr(); - return eeType.IsGeneric; + return handle.ToMethodTable()->IsGeneric; } public static bool IsGenericTypeDefinition(this RuntimeTypeHandle handle) { - EETypePtr eeType = handle.ToEETypePtr(); - return eeType.IsGenericTypeDefinition; + return handle.ToMethodTable()->IsGenericTypeDefinition; } // @@ -65,32 +58,32 @@ public static IntPtr GetRawFunctionPointerForOpenStaticDelegate(this Delegate de public static int GetValueTypeSize(this RuntimeTypeHandle handle) { - return (int)handle.ToEETypePtr().ValueTypeSize; + return (int)handle.ToMethodTable()->ValueTypeSize; } public static bool IsValueType(this RuntimeTypeHandle handle) { - return handle.ToEETypePtr().IsValueType; + return handle.ToMethodTable()->IsValueType; } public static bool IsEnum(this RuntimeTypeHandle handle) { - return handle.ToEETypePtr().IsEnum; + return handle.ToMethodTable()->IsEnum; } public static bool IsInterface(this RuntimeTypeHandle handle) { - return handle.ToEETypePtr().IsInterface; + return handle.ToMethodTable()->IsInterface; } public static bool AreTypesAssignable(RuntimeTypeHandle sourceType, RuntimeTypeHandle targetType) { - return RuntimeImports.AreTypesAssignable(sourceType.ToEETypePtr(), targetType.ToEETypePtr()); + return RuntimeImports.AreTypesAssignable(sourceType.ToMethodTable(), targetType.ToMethodTable()); } public static RuntimeTypeHandle GetTypeHandle(this object target) { - return new RuntimeTypeHandle(target.GetEETypePtr()); + return new RuntimeTypeHandle(target.GetMethodTable()); } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NativeAot.cs index 80761d024050e9..0f3c321d5cf363 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NativeAot.cs @@ -49,9 +49,9 @@ public static IntPtr OffsetOf(Type t, string fieldName) return new IntPtr(RuntimeInteropData.GetStructFieldOffset(t.TypeHandle, fieldName)); } - private static void PtrToStructureHelper(IntPtr ptr, object structure, bool allowValueClasses) + private static unsafe void PtrToStructureHelper(IntPtr ptr, object structure, bool allowValueClasses) { - if (!allowValueClasses && structure.GetEETypePtr().IsValueType) + if (!allowValueClasses && structure.GetMethodTable()->IsValueType) { throw new ArgumentException(SR.Argument_StructMustNotBeValueClass, nameof(structure)); } @@ -204,9 +204,9 @@ public static void SetLastPInvokeError(int error) PInvokeMarshal.t_lastError = error; } - internal static bool IsPinnable(object o) + internal static unsafe bool IsPinnable(object o) { - return (o == null) || !o.GetEETypePtr().ContainsGCPointers; + return (o == null) || !o.GetMethodTable()->ContainsGCPointers; } [EditorBrowsable(EditorBrowsableState.Never)] @@ -269,7 +269,7 @@ private static unsafe T ReadValueSlow(object ptr, int ofs, delegate*IsArray || ptr is string || ptr is StringBuilder) { @@ -349,7 +349,7 @@ private static unsafe void WriteValueSlow(object ptr, int ofs, T val, delegat throw new AccessViolationException(); } - if (ptr.GetEETypePtr().IsArray || + if (ptr.GetMethodTable()->IsArray || ptr is string || ptr is StringBuilder) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs index 88bcba537f932d..ccb969f1728011 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs @@ -131,7 +131,7 @@ private static IntPtr CreateReferenceTrackingHandleInternal( throw new InvalidOperationException(SR.InvalidOperation_ObjectiveCMarshalNotInitialized); } - if (!obj.GetEETypePtr().IsTrackedReferenceWithFinalizer) + if (!obj.GetMethodTable()->IsTrackedReferenceWithFinalizer) { throw new InvalidOperationException(SR.InvalidOperation_ObjectiveCTypeNoFinalizer); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs index 25800445c62ba2..2b011f01614667 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; +using Internal.Runtime; using Internal.Runtime.Augments; using Internal.Runtime.CompilerHelpers; using Internal.Runtime.CompilerServices; @@ -46,13 +47,13 @@ public static int GetHRForException(Exception e) /// Return the stub to the pinvoke marshalling stub /// /// The delegate - public static IntPtr GetFunctionPointerForDelegate(Delegate del) + public static unsafe IntPtr GetFunctionPointerForDelegate(Delegate del) { if (del == null) return IntPtr.Zero; #pragma warning disable CA2208 // Instantiate argument exceptions correctly - if (del.GetEETypePtr().IsGeneric) + if (del.GetMethodTable()->IsGeneric) throw new ArgumentException(SR.Argument_NeedNonGenericType, "delegate"); #pragma warning restore CA2208 @@ -234,7 +235,7 @@ private static PInvokeDelegateThunk AllocateThunk(Delegate del) // NativeFunctionPointerWrapper derived class // #pragma warning disable CA2208 // Instantiate argument exceptions correctly - if (delegateType.ToEETypePtr().BaseType != EETypePtr.EETypePtrOf()) + if (delegateType.ToMethodTable()->BaseType != MethodTable.Of()) throw new ArgumentException(SR.Arg_MustBeDelegate, "t"); #pragma warning restore CA2208 diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 05f48424e5916b..3b88cedcf3e1d8 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -386,9 +386,6 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary) [RuntimeImport(RuntimeLibrary, "RhTypeCast_AreTypesAssignable")] internal static extern unsafe bool AreTypesAssignable(MethodTable* pSourceType, MethodTable* pTargetType); - internal static unsafe bool AreTypesAssignable(EETypePtr pSourceType, EETypePtr pTargetType) - => AreTypesAssignable(pSourceType.ToPointer(), pTargetType.ToPointer()); - [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhTypeCast_CheckArrayStore")] internal static extern void RhCheckArrayStore(object array, object? obj); @@ -397,9 +394,6 @@ internal static unsafe bool AreTypesAssignable(EETypePtr pSourceType, EETypePtr [RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfAny")] internal static extern unsafe object IsInstanceOf(MethodTable* pTargetType, object obj); - internal static unsafe object IsInstanceOf(EETypePtr pTargetType, object obj) - => IsInstanceOf(pTargetType.ToPointer(), obj); - // // calls to runtime for allocation // These calls are needed in types which cannot use "new" to allocate and need to do it manually @@ -414,36 +408,21 @@ internal static unsafe object IsInstanceOf(EETypePtr pTargetType, object obj) [RuntimeImport(RuntimeLibrary, "RhNewObject")] internal static extern unsafe object RhNewObject(MethodTable* pEEType); - internal static unsafe object RhNewObject(EETypePtr pEEType) - => RhNewObject(pEEType.ToPointer()); - [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhNewArray")] - private static extern unsafe Array RhNewArray(MethodTable* pEEType, int length); - - internal static unsafe Array RhNewArray(EETypePtr pEEType, int length) - => RhNewArray(pEEType.ToPointer(), length); + internal static extern unsafe Array RhNewArray(MethodTable* pEEType, int length); [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhNewString")] internal static extern unsafe string RhNewString(MethodTable* pEEType, int length); - internal static unsafe string RhNewString(EETypePtr pEEType, int length) - => RhNewString(pEEType.ToPointer(), length); - [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhBox")] internal static extern unsafe object RhBox(MethodTable* pEEType, ref byte data); - internal static unsafe object RhBox(EETypePtr pEEType, ref byte data) - => RhBox(pEEType.ToPointer(), ref data); - [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhUnbox")] - private static extern unsafe void RhUnbox(object? obj, ref byte data, MethodTable* pUnboxToEEType); - - internal static unsafe void RhUnbox(object? obj, ref byte data, EETypePtr pUnboxToEEType) - => RhUnbox(obj, ref data, pUnboxToEEType.ToPointer()); + internal static extern unsafe void RhUnbox(object? obj, ref byte data, MethodTable* pUnboxToEEType); // Busy spin for the given number of iterations. [LibraryImport(RuntimeLibrary, EntryPoint = "RhSpinWait")] @@ -489,15 +468,15 @@ internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int time [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetGCDescSize")] - internal static extern int RhGetGCDescSize(EETypePtr eeType); + internal static extern unsafe int RhGetGCDescSize(MethodTable* eeType); [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhNewInterfaceDispatchCell")] - internal static extern unsafe IntPtr RhNewInterfaceDispatchCell(EETypePtr pEEType, int slotNumber); + internal static extern unsafe IntPtr RhNewInterfaceDispatchCell(MethodTable* pEEType, int slotNumber); [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhResolveDispatch")] - internal static extern IntPtr RhResolveDispatch(object pObject, EETypePtr pInterfaceType, ushort slot); + internal static extern unsafe IntPtr RhResolveDispatch(object pObject, MethodTable* pInterfaceType, ushort slot); [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhpResolveInterfaceMethod")] @@ -505,16 +484,16 @@ internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int time [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhResolveDispatchOnType")] - internal static extern unsafe IntPtr RhResolveDispatchOnType(EETypePtr instanceType, EETypePtr interfaceType, ushort slot, EETypePtr* pGenericContext); + internal static extern unsafe IntPtr RhResolveDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot, MethodTable** pGenericContext); - internal static unsafe IntPtr RhResolveDispatchOnType(EETypePtr instanceType, EETypePtr interfaceType, ushort slot) + internal static unsafe IntPtr RhResolveDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot) { return RhResolveDispatchOnType(instanceType, interfaceType, slot, null); } [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetRuntimeHelperForType")] - internal static extern unsafe IntPtr RhGetRuntimeHelperForType(EETypePtr pEEType, RuntimeHelperKind kind); + internal static extern unsafe IntPtr RhGetRuntimeHelperForType(MethodTable* pEEType, RuntimeHelperKind kind); // // Support for GC and HandleTable callouts. @@ -538,11 +517,11 @@ internal enum GcRestrictedCalloutKind [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhRegisterRefCountedHandleCallback")] - internal static extern bool RhRegisterRefCountedHandleCallback(IntPtr pCalloutMethod, EETypePtr pTypeFilter); + internal static extern unsafe bool RhRegisterRefCountedHandleCallback(IntPtr pCalloutMethod, MethodTable* pTypeFilter); [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhUnregisterRefCountedHandleCallback")] - internal static extern void RhUnregisterRefCountedHandleCallback(IntPtr pCalloutMethod, EETypePtr pTypeFilter); + internal static extern unsafe void RhUnregisterRefCountedHandleCallback(IntPtr pCalloutMethod, MethodTable* pTypeFilter); #if FEATURE_OBJCMARSHAL [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index fb8db35f2d2699..c5d13a97abd3fb 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Threading; +using Internal.Runtime; using Internal.Runtime.Augments; namespace System.Runtime @@ -106,7 +107,7 @@ private static unsafe IntPtr GVMLookupForSlotSlow(object obj, RuntimeMethodHandl { Value v = CacheMiss((IntPtr)obj.GetMethodTable(), RuntimeMethodHandle.ToIntPtr(slot), (IntPtr context, IntPtr signature, object contextObject, ref IntPtr auxResult) - => RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget(new RuntimeTypeHandle(new EETypePtr(context)), *(RuntimeMethodHandle*)&signature)); + => RuntimeAugments.TypeLoaderCallbacks.ResolveGenericVirtualMethodTarget(new RuntimeTypeHandle((MethodTable*)context), *(RuntimeMethodHandle*)&signature)); return v._result; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs index e9fca05ca80124..bb9cd160986db2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs @@ -52,7 +52,7 @@ internal void Free() private static bool DoNotThrowForAssembly => AppContext.TryGetSwitch("Switch.System.Reflection.Disabled.DoNotThrowForAssembly", out bool doNotThrow) && doNotThrow; private static bool DoNotThrowForAttributes => AppContext.TryGetSwitch("Switch.System.Reflection.Disabled.DoNotThrowForAttributes", out bool doNotThrow) && doNotThrow; - internal EETypePtr ToEETypePtrMayBeNull() => new EETypePtr(_pUnderlyingEEType); + internal MethodTable* ToMethodTableMayBeNull() => _pUnderlyingEEType; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal RuntimeTypeInfo GetRuntimeTypeInfo() @@ -161,13 +161,13 @@ public override bool IsEnumDefined(object value) if (value is Enum) { - if (value.GetEETypePtr() != this.ToEETypePtrMayBeNull()) + if (value.GetMethodTable() != this.ToMethodTableMayBeNull()) throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, value.GetType(), this)); } else { Type underlyingType = Enum.InternalGetUnderlyingType(this); - if (!(underlyingType.TypeHandle.ToEETypePtr() == value.GetEETypePtr())) + if (!(underlyingType.TypeHandle.ToMethodTable() == value.GetMethodTable())) throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), underlyingType)); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs index cf48b0aedc7549..06c61be08a8fff 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeTypeHandle.cs @@ -21,9 +21,6 @@ public unsafe struct RuntimeTypeHandle : IEquatable, ISeriali internal unsafe RuntimeTypeHandle(MethodTable* pEEType) => _value = (IntPtr)pEEType; - internal RuntimeTypeHandle(EETypePtr pEEType) - => _value = pEEType.RawValue; - private RuntimeTypeHandle(IntPtr value) => _value = value; @@ -36,12 +33,12 @@ public override bool Equals(object? obj) return false; } - public override int GetHashCode() + public override unsafe int GetHashCode() { if (IsNull) return 0; - return this.ToEETypePtr().GetHashCode(); + return (int)this.ToMethodTable()->HashCode; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -100,12 +97,6 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) throw new PlatformNotSupportedException(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal EETypePtr ToEETypePtr() - { - return new EETypePtr(_value); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MethodTable* ToMethodTable() { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.NativeAot.cs index 5e9344ea291922..a8efc76018a30d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/String.NativeAot.cs @@ -5,6 +5,8 @@ using System.Runtime; using System.Runtime.CompilerServices; +using Internal.Runtime; + namespace System { // This class is marked EagerStaticClassConstruction because it's nice to have this @@ -16,12 +18,12 @@ public partial class String [Intrinsic] public static readonly string Empty = ""; - internal static string FastAllocateString(int length) + internal static unsafe string FastAllocateString(int length) { // We allocate one extra char as an interop convenience so that our strings are null- // terminated, however, we don't pass the extra +1 to the string allocation because the base // size of this object includes the _firstChar field. - string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf(), length); + string newStr = RuntimeImports.RhNewString(MethodTable.Of(), length); Debug.Assert(newStr._stringLength == length); return newStr; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs index ad29037c586dae..3b209cd489c44a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/TypedReference.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using Internal.Reflection.Augments; +using Internal.Runtime; using Internal.Runtime.CompilerServices; namespace System @@ -47,20 +48,20 @@ internal static RuntimeTypeHandle RawTargetTypeToken(TypedReference value) return value._typeHandle; } - public static object ToObject(TypedReference value) + public static unsafe object ToObject(TypedReference value) { RuntimeTypeHandle typeHandle = value._typeHandle; if (typeHandle.IsNull) ThrowHelper.ThrowArgumentException_ArgumentNull_TypedRefType(); - EETypePtr eeType = typeHandle.ToEETypePtr(); - if (eeType.IsValueType) + MethodTable* eeType = typeHandle.ToMethodTable(); + if (eeType->IsValueType) { return RuntimeImports.RhBox(eeType, ref value.Value); } - else if (eeType.IsPointer || eeType.IsFunctionPointer) + else if (eeType->IsPointer || eeType->IsFunctionPointer) { - return RuntimeImports.RhBox(EETypePtr.EETypePtrOf(), ref value.Value); + return RuntimeImports.RhBox(MethodTable.Of(), ref value.Value); } else { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs index 4a3ad1e546c7bc..e8340e41191513 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs @@ -49,7 +49,7 @@ internal virtual unsafe int __GetFieldHelper(int index, out MethodTable* mt) public override unsafe bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || obj.GetEETypePtr() != this.GetEETypePtr()) + if (obj == null || obj.GetMethodTable() != this.GetMethodTable()) return false; int numFields = __GetFieldHelper(GetNumFields, out _); @@ -60,10 +60,10 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) if (numFields == UseFastHelper) { // Sanity check - if there are GC references, we should not be comparing bytes - Debug.Assert(!this.GetEETypePtr().ContainsGCPointers); + Debug.Assert(!this.GetMethodTable()->ContainsGCPointers); // Compare the memory - int valueTypeSize = (int)this.GetEETypePtr().ValueTypeSize; + int valueTypeSize = (int)this.GetMethodTable()->ValueTypeSize; return SpanHelpers.SequenceEqual(ref thisRawData, ref thatRawData, valueTypeSize); } else @@ -93,9 +93,9 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) return true; } - public override int GetHashCode() + public override unsafe int GetHashCode() { - int hashCode = this.GetEETypePtr().GetHashCode(); + int hashCode = (int)this.GetMethodTable()->HashCode; hashCode ^= GetHashCodeImpl(); diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs index 70d82828cd008e..539418fc76c308 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -76,16 +76,10 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe [RuntimeImport(RuntimeLibrary, "RhNewObject")] private static extern unsafe object RhNewObject(MethodTable* pEEType); - internal static unsafe object RhNewObject(EETypePtr pEEType) - => RhNewObject(pEEType.ToPointer()); - [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhNewArray")] private static extern unsafe Array RhNewArray(MethodTable* pEEType, int length); - internal static unsafe Array RhNewArray(EETypePtr pEEType, int length) - => RhNewArray(pEEType.ToPointer(), length); - [DllImport(RuntimeLibrary)] internal static extern unsafe void RhAllocateNewObject(IntPtr pEEType, uint flags, void* pResult); diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index 1b61079464ee81..ec2e93c06ad04e 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -160,9 +160,6 @@ Runtime.Base\src\System\Runtime\CompilerServices\IsByRefLikeAttribute.cs - - System\EETypePtr.cs - System\Runtime\ExceptionIDs.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index bd4595f45137c1..7ae867370bdf40 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -1354,13 +1354,12 @@ private static bool IsActivatorAllocatorOf(MethodDesc method) private static bool IsEETypePtrOf(MethodDesc method) { - if (method.IsIntrinsic && (method.Name == "EETypePtrOf" || method.Name == "Of") && method.Instantiation.Length == 1) + if (method.IsIntrinsic && method.Name == "Of" && method.Instantiation.Length == 1) { MetadataType owningType = method.OwningType as MetadataType; if (owningType != null) { - return (owningType.Name == "EETypePtr" && owningType.Namespace == "System") - || (owningType.Name == "MethodTable" && owningType.Namespace == "Internal.Runtime"); + return owningType.Name == "MethodTable" && owningType.Namespace == "Internal.Runtime"; } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index b00f3f00a47372..e21640cbdbc24f 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -1824,7 +1824,6 @@ private void expandRawHandleIntrinsic(ref CORINFO_RESOLVED_TOKEN pResolvedToken, switch (method.Name) { - case "EETypePtrOf": case "Of": ComputeLookup(ref pResolvedToken, method.Instantiation[0], ReadyToRunHelperId.TypeHandle, ref pResult.lookup); pResult.handleType = CorInfoGenericHandleType.CORINFO_HANDLETYPE_CLASS; From b8114f9c81897e9dbbda24f7e7a30ea0c04a8d35 Mon Sep 17 00:00:00 2001 From: Tom McDonald Date: Sun, 21 Jan 2024 08:05:01 -0500 Subject: [PATCH 153/189] Revert pr#96099 (#97264) * Revert "Forward minipal_get_length_utf16_to_utf8 and minipal_convert_utf16_to_utf8 from DAC (#97055)" This reverts commit 1263107c3b3dd2f50250b88a9827b4d77fe04110. * Revert "Cleanup some string operating functions (#96099)" This reverts commit bc81c550ae7c23a4f8f04004ca11545a1650a2ca. --- src/coreclr/debug/daccess/inspect.cpp | 4 +- src/coreclr/debug/daccess/stack.cpp | 2 +- src/coreclr/debug/daccess/stdafx.h | 5 + src/coreclr/debug/daccess/task.cpp | 5 +- .../dlls/mscordac/mscordac_unixexports.src | 3 - src/coreclr/ilasm/main.cpp | 1 + src/coreclr/inc/clr/fs/path.h | 2 + src/coreclr/inc/corhlprpriv.h | 64 +++- src/coreclr/inc/fstring.h | 44 +++ src/coreclr/inc/utilcode.h | 21 ++ src/coreclr/minipal/Unix/CMakeLists.txt | 6 - src/coreclr/pal/inc/strsafe.h | 285 ++++++++++++++++ src/coreclr/utilcode/CMakeLists.txt | 1 + src/coreclr/utilcode/fstring.cpp | 321 ++++++++++++++++++ src/coreclr/utilcode/sstring.cpp | 27 +- src/coreclr/vm/classcompat.cpp | 2 +- src/coreclr/vm/classhash.cpp | 1 + src/coreclr/vm/codeman.cpp | 1 + src/coreclr/vm/methodtablebuilder.cpp | 6 +- 19 files changed, 757 insertions(+), 44 deletions(-) create mode 100644 src/coreclr/inc/fstring.h create mode 100644 src/coreclr/pal/inc/strsafe.h create mode 100644 src/coreclr/utilcode/fstring.cpp diff --git a/src/coreclr/debug/daccess/inspect.cpp b/src/coreclr/debug/daccess/inspect.cpp index a7f697ec0b7c27..ec8356cf1f519d 100644 --- a/src/coreclr/debug/daccess/inspect.cpp +++ b/src/coreclr/debug/daccess/inspect.cpp @@ -1049,8 +1049,8 @@ ClrDataValue::GetString( { *strLen = static_cast(u16_strlen(msgStr) + 1); } - - status = u16_strcpy_s(str, bufLen, msgStr) != NULL ? S_OK : S_FALSE; + status = StringCchCopy(str, bufLen, msgStr) == S_OK ? + S_OK : S_FALSE; } else { diff --git a/src/coreclr/debug/daccess/stack.cpp b/src/coreclr/debug/daccess/stack.cpp index 43b420fd6e7f64..6b9f1a491c291c 100644 --- a/src/coreclr/debug/daccess/stack.cpp +++ b/src/coreclr/debug/daccess/stack.cpp @@ -830,7 +830,7 @@ ClrDataFrame::GetArgumentByIndex( *nameLen = 5; } - u16_strcpy_s(name, bufLen, W("this")); + StringCchCopy(name, bufLen, W("this")); } else { diff --git a/src/coreclr/debug/daccess/stdafx.h b/src/coreclr/debug/daccess/stdafx.h index 14f6882d808eca..bb7b7b2365de59 100644 --- a/src/coreclr/debug/daccess/stdafx.h +++ b/src/coreclr/debug/daccess/stdafx.h @@ -50,6 +50,11 @@ #include "dacimpl.h" +#define STRSAFE_NO_DEPRECATE +#include +#undef _ftcscat +#undef _ftcscpy + // from ntstatus.h #define STATUS_STOWED_EXCEPTION ((NTSTATUS)0xC000027BL) diff --git a/src/coreclr/debug/daccess/task.cpp b/src/coreclr/debug/daccess/task.cpp index b54aab734bceb0..ddbf251b7b9825 100644 --- a/src/coreclr/debug/daccess/task.cpp +++ b/src/coreclr/debug/daccess/task.cpp @@ -732,7 +732,8 @@ ClrDataAppDomain::GetName( } else { - status = u16_strcpy_s(name, bufLen, (PCWSTR)rawName) != NULL ? S_OK : S_FALSE; + status = StringCchCopy(name, bufLen, (PCWSTR)rawName) == S_OK ? + S_OK : S_FALSE; if (nameLen) { size_t cchName = u16_strlen((PCWSTR)rawName) + 1; @@ -4767,7 +4768,7 @@ ClrDataExceptionState::GetString( message->GetStringLength(), true); - status = u16_strcpy_s(str, bufLen, msgStr) != NULL ? S_OK : S_FALSE; + status = StringCchCopy(str, bufLen, msgStr) == S_OK ? S_OK : S_FALSE; if (strLen != NULL) { size_t cchName = u16_strlen(msgStr) + 1; diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 5a9b1e40970e87..6cdd5ff733bcf8 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -69,9 +69,6 @@ nativeStringResourceTable_mscorrc #PAL__pread #PAL__close -#minipal_get_length_utf16_to_utf8 -#minipal_convert_utf16_to_utf8 - #_wcsicmp #_stricmp #sprintf_s diff --git a/src/coreclr/ilasm/main.cpp b/src/coreclr/ilasm/main.cpp index ebe1a2c220e550..838f05aa996cf8 100644 --- a/src/coreclr/ilasm/main.cpp +++ b/src/coreclr/ilasm/main.cpp @@ -11,6 +11,7 @@ #include "clrversion.h" #include "shimload.h" +#include "strsafe.h" #define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS((expr)) WCHAR* EqualOrColon(_In_ __nullterminated WCHAR* szArg) diff --git a/src/coreclr/inc/clr/fs/path.h b/src/coreclr/inc/clr/fs/path.h index a42b1d48500db9..efc21a5cdd4392 100644 --- a/src/coreclr/inc/clr/fs/path.h +++ b/src/coreclr/inc/clr/fs/path.h @@ -10,6 +10,8 @@ #include "clrtypes.h" +#include "strsafe.h" + #include "clr/str.h" namespace clr diff --git a/src/coreclr/inc/corhlprpriv.h b/src/coreclr/inc/corhlprpriv.h index f42eaa637d4e7f..62298798d70178 100644 --- a/src/coreclr/inc/corhlprpriv.h +++ b/src/coreclr/inc/corhlprpriv.h @@ -11,7 +11,7 @@ #define __CORHLPRPRIV_H__ #include "corhlpr.h" -#include +#include "fstring.h" #if defined(_MSC_VER) && defined(HOST_X86) #pragma optimize("y", on) // If routines don't get inlined, don't pay the EBP frame penalty @@ -225,33 +225,71 @@ class CQuickMemoryBase iSize = cbTotal; } + + // Convert UTF8 string to UNICODE string, optimized for speed + HRESULT ConvertUtf8_UnicodeNoThrow(const char * utf8str) + { + bool allAscii; + DWORD length; + + HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length); + + if (SUCCEEDED(hr)) + { + LPWSTR buffer = (LPWSTR) AllocNoThrow((length + 1) * sizeof(WCHAR)); + + if (buffer == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length); + } + } + + return hr; + } + // Convert UTF8 string to UNICODE string, optimized for speed void ConvertUtf8_Unicode(const char * utf8str) { - size_t sourceLen = strlen(utf8str); - size_t destLen = minipal_get_length_utf8_to_utf16(utf8str, sourceLen, 0); + bool allAscii; + DWORD length; - CHAR16_T* buffer = (CHAR16_T*) AllocThrows((destLen + 1) * sizeof(CHAR16_T)); - buffer[destLen] = W('\0'); + HRESULT hr = FString::Utf8_Unicode_Length(utf8str, & allAscii, & length); - if (!minipal_convert_utf8_to_utf16(utf8str, sourceLen, buffer, destLen, 0)) + if (SUCCEEDED(hr)) { - ThrowHR(EMAKEHR(errno)); + LPWSTR buffer = (LPWSTR) AllocThrows((length + 1) * sizeof(WCHAR)); + + hr = FString::Utf8_Unicode(utf8str, allAscii, buffer, length); + } + + if (FAILED(hr)) + { + ThrowHR(hr); } } // Convert UNICODE string to UTF8 string, optimized for speed void ConvertUnicode_Utf8(const WCHAR * pString) { - size_t sourceLen = u16_strlen(pString); - size_t destLen = minipal_get_length_utf16_to_utf8((const CHAR16_T*)pString, sourceLen, 0); + bool allAscii; + DWORD length; + + HRESULT hr = FString::Unicode_Utf8_Length(pString, & allAscii, & length); - LPSTR buffer = (LPSTR) AllocThrows((destLen + 1) * sizeof(char)); - buffer[destLen] = '\0'; + if (SUCCEEDED(hr)) + { + LPSTR buffer = (LPSTR) AllocThrows((length + 1) * sizeof(char)); + + hr = FString::Unicode_Utf8(pString, allAscii, buffer, length); + } - if (!minipal_convert_utf16_to_utf8((const CHAR16_T*)pString, sourceLen, buffer, destLen, 0)) + if (FAILED(hr)) { - ThrowHR(EMAKEHR(errno)); + ThrowHR(hr); } } diff --git a/src/coreclr/inc/fstring.h b/src/coreclr/inc/fstring.h new file mode 100644 index 00000000000000..1be37e242974c3 --- /dev/null +++ b/src/coreclr/inc/fstring.h @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// --------------------------------------------------------------------------- +// FString.h (Fast String) +// + +// --------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------ +// FString is fast string handling namespace + + +// 1) Simple +// 2) No C++ exception +// 3) Optimized for speed + + +#ifndef _FSTRING_H_ +#define _FSTRING_H_ + +namespace FString +{ + // Note: All "length" parameters do not count the space for the null terminator. + // Caller of Unicode_Utf8 and Utf8_Unicode must pass in a buffer of size at least length + 1. + + // Scan for ASCII only string, calculate result UTF8 string length + HRESULT Unicode_Utf8_Length(_In_z_ LPCWSTR pString, _Out_ bool * pAllAscii, _Out_ DWORD * pLength); + + // Convert UNICODE string to UTF8 string. Direct/fast conversion if ASCII + HRESULT Unicode_Utf8(_In_z_ LPCWSTR pString, bool allAscii, _Out_writes_bytes_(length) LPSTR pBuffer, DWORD length); + + // Scan for ASCII string, calculate result UNICODE string length + HRESULT Utf8_Unicode_Length(_In_z_ LPCSTR pString, _Out_ bool * pAllAscii, _Out_ DWORD * pLength); + + // Convert UTF8 string to UNICODE. Direct/fast conversion if ASCII + HRESULT Utf8_Unicode(_In_z_ LPCSTR pString, bool allAscii, _Out_writes_bytes_(length) LPWSTR pBuffer, DWORD length); + + HRESULT ConvertUnicode_Utf8(_In_z_ LPCWSTR pString, _Outptr_result_z_ LPSTR * pBuffer); + + HRESULT ConvertUtf8_Unicode(_In_z_ LPCSTR pString, _Outptr_result_z_ LPWSTR * pBuffer); + +} // namespace FString + +#endif // _FSTRING_H_ diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index cbcd37fa0fb4fe..fe5db13f6b9719 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -2834,6 +2834,27 @@ template class CChainedHash }; +//***************************************************************************** +// +//********** String helper functions. +// +//***************************************************************************** + +//***************************************************************************** +// Checks if string length exceeds the specified limit +//***************************************************************************** +inline BOOL IsStrLongerThan(_In_ _In_z_ char* pstr, unsigned N) +{ + LIMITED_METHOD_CONTRACT; + unsigned i = 0; + if(pstr) + { + for(i=0; (i < N)&&(pstr[i]); i++); + } + return (i >= N); +} + + //***************************************************************************** // Class to parse a list of simple assembly names and then find a match //***************************************************************************** diff --git a/src/coreclr/minipal/Unix/CMakeLists.txt b/src/coreclr/minipal/Unix/CMakeLists.txt index 944c9c3a66759b..ca41eb4e2bb0d8 100644 --- a/src/coreclr/minipal/Unix/CMakeLists.txt +++ b/src/coreclr/minipal/Unix/CMakeLists.txt @@ -7,12 +7,6 @@ if(NOT CLR_CROSS_COMPONENTS_BUILD) list(APPEND SOURCES ${CLR_SRC_NATIVE_DIR}/minipal/cpufeatures.c ) - - if(CLR_CMAKE_HOST_OSX) - list(APPEND SOURCES - ${CLR_SRC_NATIVE_DIR}/minipal/utf8.c - ) - endif() endif() add_library(coreclrminipal diff --git a/src/coreclr/pal/inc/strsafe.h b/src/coreclr/pal/inc/strsafe.h new file mode 100644 index 00000000000000..b69feb73c25129 --- /dev/null +++ b/src/coreclr/pal/inc/strsafe.h @@ -0,0 +1,285 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*++ + + + + + +--*/ + +/****************************************************************** +* * +* strsafe.h -- This module defines safer C library string * +* routine replacements. These are meant to make C * +* a bit more safe in reference to security and * +* robustness * +* * +******************************************************************/ +#ifndef _STRSAFE_H_INCLUDED_ +#define _STRSAFE_H_INCLUDED_ +#ifdef _MSC_VER +#pragma once +#endif + +#include // for _vsnprintf, getc, getwc +#include // for memset +#include // for va_start, etc. + +#ifndef _SIZE_T_DEFINED +#ifdef HOST_64BIT +typedef unsigned __int64 size_t; +#else +typedef __w64 unsigned int size_t; +#endif // !HOST_64BIT +#define _SIZE_T_DEFINED +#endif // !_SIZE_T_DEFINED + +#ifndef SUCCEEDED +#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0) +#endif + +#ifndef FAILED +#define FAILED(hr) ((HRESULT)(hr) < 0) +#endif + +#ifndef S_OK +#define S_OK ((HRESULT)0x00000000L) +#endif + +#ifdef __cplusplus +#define _STRSAFE_EXTERN_C extern "C" +#else +#define _STRSAFE_EXTERN_C extern +#endif + +// If you do not want to use these functions inline (and instead want to link w/ strsafe.lib), then +// #define STRSAFE_LIB before including this header file. +#if defined(STRSAFE_LIB) +#define STRSAFEAPI _STRSAFE_EXTERN_C HRESULT __stdcall +#pragma comment(lib, "strsafe.lib") +#elif defined(STRSAFE_LIB_IMPL) +#define STRSAFEAPI _STRSAFE_EXTERN_C HRESULT __stdcall +#else +#define STRSAFEAPI __inline HRESULT __stdcall +#define STRSAFE_INLINE +#endif + +// Some functions always run inline because they use stdin and we want to avoid building multiple +// versions of strsafe lib depending on if you use msvcrt, libcmt, etc. +#define STRSAFE_INLINE_API __inline HRESULT __stdcall + +// The user can request no "Cb" or no "Cch" fuctions, but not both! +#if defined(STRSAFE_NO_CB_FUNCTIONS) && defined(STRSAFE_NO_CCH_FUNCTIONS) +#error cannot specify both STRSAFE_NO_CB_FUNCTIONS and STRSAFE_NO_CCH_FUNCTIONS !! +#endif + +// This should only be defined when we are building strsafe.lib +#ifdef STRSAFE_LIB_IMPL +#define STRSAFE_INLINE +#endif + + +#define STRSAFE_MAX_CCH 2147483647 // max # of characters we support (same as INT_MAX) + +// STRSAFE error return codes +// +#define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007AL) // 0x7A = 122L = ERROR_INSUFFICIENT_BUFFER +#define STRSAFE_E_INVALID_PARAMETER ((HRESULT)0x80070057L) // 0x57 = 87L = ERROR_INVALID_PARAMETER +#define STRSAFE_E_END_OF_FILE ((HRESULT)0x80070026L) // 0x26 = 38L = ERROR_HANDLE_EOF + +// Flags for controling the Ex functions +// +// STRSAFE_FILL_BYTE(0xFF) 0x000000FF // bottom byte specifies fill pattern +#define STRSAFE_IGNORE_NULLS 0x00000100 // treat null as TEXT("") -- don't fault on NULL buffers +#define STRSAFE_FILL_BEHIND_NULL 0x00000200 // fill in extra space behind the null terminator +#define STRSAFE_FILL_ON_FAILURE 0x00000400 // on failure, overwrite pszDest with fill pattern and null terminate it +#define STRSAFE_NULL_ON_FAILURE 0x00000800 // on failure, set *pszDest = TEXT('\0') +#define STRSAFE_NO_TRUNCATION 0x00001000 // instead of returning a truncated result, copy/append nothing to pszDest and null terminate it + +#define STRSAFE_VALID_FLAGS (0x000000FF | STRSAFE_IGNORE_NULLS | STRSAFE_FILL_BEHIND_NULL | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION) + +// helper macro to set the fill character and specify buffer filling +#define STRSAFE_FILL_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_BEHIND_NULL)) +#define STRSAFE_FAILURE_BYTE(x) ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_ON_FAILURE)) + +#define STRSAFE_GET_FILL_PATTERN(dwFlags) ((int)(dwFlags & 0x000000FF)) + +// prototypes for the worker functions +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc); +STRSAFEAPI StringCopyWorkerW(WCHAR* pszDest, size_t cchDest, const WCHAR* pszSrc); +#endif // STRSAFE_INLINE + +#ifndef STRSAFE_NO_CCH_FUNCTIONS +/*++ + +STDAPI StringCchCopy(LPTSTR pszDest, + size_t cchDest, + LPCTSTR pszSrc); + +Routine Description: + + This routine is a safer version of the C built-in function 'strcpy'. + The size of the destination buffer (in characters) is a parameter and + this function will not write past the end of this buffer and it will + ALWAYS null terminate the destination buffer (unless it is zero length). + + This routine is not a replacement for strncpy. That function will pad the + destination string with extra null termination characters if the count is + greater than the length of the source string, and it will fail to null + terminate the destination string if the source string length is greater + than or equal to the count. You can not blindly use this instead of strncpy: + it is common for code to use it to "patch" strings and you would introduce + errors if the code started null terminating in the middle of the string. + + This function returns a hresult, and not a pointer. It returns a S_OK + if the string was copied without truncation and null terminated, otherwise + it will return a failure code. In failure cases as much of pszSrc will be + copied to pszDest as possible, and pszDest will be null terminated. + +Arguments: + + pszDest - destination string + + cchDest - size of destination buffer in characters. + length must be = (_tcslen(src) + 1) to hold all of the + source including the null terminator + + pszSrc - source string which must be null terminated + +Notes: + Behavior is undefined if source and destination strings overlap. + + pszDest and pszSrc should not be NULL. See StringCchCopyEx if you require + the handling of NULL values. + +Return Value: + + S_OK - if there was source data and it was all copied and the + resultant dest string was null terminated + + failure - you can use the macro HRESULT_CODE() to get a win32 error + code for all hresult failure cases + + STRSAFE_E_INSUFFICIENT_BUFFER / + HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER + - this return value is an indication that the copy operation + failed due to insufficient space. When this error occurs, + the destination buffer is modified to contain a truncated + version of the ideal result and is null terminated. This + is useful for situations where truncation is ok + + It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the + return value of this function. + +--*/ + +STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc); +STRSAFEAPI StringCchCopyW(WCHAR* pszDest, size_t cchDest, const WCHAR* pszSrc); +#ifdef UNICODE +#define StringCchCopy StringCchCopyW +#else +#define StringCchCopy StringCchCopyA +#endif // !UNICODE + +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyWorkerA(pszDest, cchDest, pszSrc); + } + + return hr; +} + +STRSAFEAPI StringCchCopyW(WCHAR* pszDest, size_t cchDest, const WCHAR* pszSrc) +{ + HRESULT hr; + + if (cchDest > STRSAFE_MAX_CCH) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = StringCopyWorkerW(pszDest, cchDest, pszSrc); + } + + return hr; +} +#endif // STRSAFE_INLINE +#endif // !STRSAFE_NO_CCH_FUNCTIONS + +// these are the worker functions that actually do the work +#ifdef STRSAFE_INLINE +STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc) +{ + HRESULT hr = S_OK; + + if (cchDest == 0) + { + // can not null terminate a zero-byte dest buffer + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + while (cchDest && (*pszSrc != '\0')) + { + *pszDest++ = *pszSrc++; + cchDest--; + } + + if (cchDest == 0) + { + // we are going to truncate pszDest + pszDest--; + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + *pszDest= '\0'; + } + + return hr; +} + +STRSAFEAPI StringCopyWorkerW(WCHAR* pszDest, size_t cchDest, const WCHAR* pszSrc) +{ + HRESULT hr = S_OK; + + if (cchDest == 0) + { + // can not null terminate a zero-byte dest buffer + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + while (cchDest && (*pszSrc != L'\0')) + { + *pszDest++ = *pszSrc++; + cchDest--; + } + + if (cchDest == 0) + { + // we are going to truncate pszDest + pszDest--; + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + + *pszDest= L'\0'; + } + + return hr; +} +#endif // STRSAFE_INLINE + +#endif // _STRSAFE_H_INCLUDED_ diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index 1206b5237b9ddb..ec543e707d7185 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -6,6 +6,7 @@ set(UTILCODE_COMMON_SOURCES ex.cpp sbuffer.cpp sstring_com.cpp + fstring.cpp namespaceutil.cpp makepath.cpp splitpath.cpp diff --git a/src/coreclr/utilcode/fstring.cpp b/src/coreclr/utilcode/fstring.cpp new file mode 100644 index 00000000000000..9bcd12d1fffc6c --- /dev/null +++ b/src/coreclr/utilcode/fstring.cpp @@ -0,0 +1,321 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// --------------------------------------------------------------------------- +// FString.cpp +// + +// --------------------------------------------------------------------------- + +#include "stdafx.h" +#include "ex.h" +#include "holder.h" + +#include "fstring.h" + + +namespace FString +{ + +#ifdef _MSC_VER +#pragma optimize("t", on) +#endif // _MSC_VER + +#define MAX_LENGTH 0x1fffff00 + + +HRESULT Unicode_Utf8_Length(_In_z_ LPCWSTR pString, _Out_ bool * pAllAscii, _Out_ DWORD * pLength) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + * pAllAscii = true; + + LPCWSTR p = pString; + + while (true) + { + WCHAR ch = * p; + + // Single check for termination and non ASCII + if (((unsigned) (ch - 1)) >= 0x7F) + { + if (ch != 0) + { + * pAllAscii = false; + } + + break; + } + + p ++; + } + + if (* pAllAscii) + { + if ((p - pString) > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + + * pLength = (DWORD) (p - pString); + } + else // use WideCharToMultiByte to calculate result length + { + * pLength = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, NULL, 0, NULL, NULL); + + if (*pLength == 0) + { + return HRESULT_FROM_GetLastError(); + } + + // Remove the count of null terminator, to be consistent with the all-ASCII case. + --*pLength; + + if (*pLength > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + } + + return S_OK; +} + + +// UNICODE to UTF8 +HRESULT Unicode_Utf8(_In_z_ LPCWSTR pString, bool allAscii, _Out_writes_bytes_(length) LPSTR pBuffer, DWORD length) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + pBuffer[length] = 0; + + if (allAscii) + { + LPCWSTR p = pString; + + LPSTR q = pBuffer; + + LPCWSTR endP = p + length - 8; + + // Unfold to optimize for long string: 8 chars per iteration + while (p < endP) + { + q[0] = (char) p[0]; + q[1] = (char) p[1]; + q[2] = (char) p[2]; + q[3] = (char) p[3]; + + q[4] = (char) p[4]; + q[5] = (char) p[5]; + q[6] = (char) p[6]; + q[7] = (char) p[7]; + + q += 8; + p += 8; + } + + endP += 8; + + while (p < endP) + { + * q ++ = (char) * p ++; + } + } + else + { + length = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, pBuffer, (int) length + 1, NULL, NULL); + + if (length == 0) + { + return HRESULT_FROM_GetLastError(); + } + } + + return S_OK; +} + + +HRESULT Utf8_Unicode_Length(_In_z_ LPCSTR pString, _Out_ bool * pAllAscii, _Out_ DWORD * pLength) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + * pAllAscii = true; + + LPCSTR p = pString; + + while (true) + { + char ch = * p; + + // Single check for termination and non ASCII + if (((unsigned) (ch - 1)) >= 0x7F) + { + if (ch != 0) + { + * pAllAscii = false; + } + + break; + } + + p ++; + } + + if (* pAllAscii) + { + if ((p - pString) > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + + * pLength = (DWORD)(p - pString); + } + else + { + * pLength = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, NULL, 0); + + if (* pLength == 0) + { + return HRESULT_FROM_GetLastError(); + } + + // Remove the count of null terminator, to be consistent with the all-ASCII case. + --*pLength; + + if (* pLength > MAX_LENGTH) + { + return COR_E_OVERFLOW; + } + } + + return S_OK; +} + + +// UTF8 to Unicode + +HRESULT Utf8_Unicode(_In_z_ LPCSTR pString, bool allAscii, _Out_writes_bytes_(length) LPWSTR pBuffer, DWORD length) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + pBuffer[length] = 0; + + if (allAscii) + { + LPCSTR p = pString; + + LPWSTR q = pBuffer; + + LPCSTR endP = p + length - 8; + + // Unfold to optimize for long string: 4 chars per iteration + while (p < endP) + { + q[0] = (WCHAR) p[0]; + q[1] = (WCHAR) p[1]; + q[2] = (WCHAR) p[2]; + q[3] = (WCHAR) p[3]; + + q[4] = (WCHAR) p[4]; + q[5] = (WCHAR) p[5]; + q[6] = (WCHAR) p[6]; + q[7] = (WCHAR) p[7]; + + q += 8; + p += 8; + } + + endP += 8; + + while (p < endP) + { + * q ++ = (WCHAR) * p ++; + } + } + else + { + length = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, pBuffer, (int) length + 1); + + if (length == 0) + { + return HRESULT_FROM_GetLastError(); + } + } + + return S_OK; +} + + +HRESULT ConvertUnicode_Utf8(_In_z_ LPCWSTR pString, _Outptr_result_z_ LPSTR * pBuffer) +{ + bool allAscii; + DWORD length; + + HRESULT hr = Unicode_Utf8_Length(pString, & allAscii, & length); + + if (SUCCEEDED(hr)) + { + * pBuffer = new (nothrow) char[length + 1]; + + if (* pBuffer == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = Unicode_Utf8(pString, allAscii, * pBuffer, length); + } + } + + return hr; +} + + +HRESULT ConvertUtf8_Unicode(_In_z_ LPCSTR pString, _Outptr_result_z_ LPWSTR * pBuffer) +{ + bool allAscii; + DWORD length; + + HRESULT hr = Utf8_Unicode_Length(pString, & allAscii, & length); + + if (SUCCEEDED(hr)) + { + * pBuffer = new (nothrow) WCHAR[length + 1]; + + if (* pBuffer == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = Utf8_Unicode(pString, allAscii, * pBuffer, length); + } + } + + return hr; +} + + +#ifdef _MSC_VER +#pragma optimize("", on) +#endif // _MSC_VER + +} // namespace FString diff --git a/src/coreclr/utilcode/sstring.cpp b/src/coreclr/utilcode/sstring.cpp index e20e361fe59895..d87efd8e10f0b8 100644 --- a/src/coreclr/utilcode/sstring.cpp +++ b/src/coreclr/utilcode/sstring.cpp @@ -10,7 +10,6 @@ #include "sstring.h" #include "ex.h" #include "holder.h" -#include #if defined(_MSC_VER) @@ -865,25 +864,27 @@ COUNT_T SString::ConvertToUTF8(SString &s) const UNREACHABLE(); } - size_t length = minipal_get_length_utf16_to_utf8((CHAR16_T*)GetRawUnicode(), GetCount(), MINIPAL_MB_NO_REPLACE_INVALID_CHARS); + // @todo: use WC_NO_BEST_FIT_CHARS + bool allAscii; + DWORD length; - if (length >= COUNT_T_MAX) - { - ThrowHR(COR_E_OVERFLOW); - } - - s.Resize((COUNT_T)length, REPRESENTATION_UTF8); + HRESULT hr = FString::Unicode_Utf8_Length(GetRawUnicode(), & allAscii, & length); - //we optimize the empty string by replacing it with null for SString above in Resize - if (length > 0) + if (SUCCEEDED(hr)) { - if (!minipal_convert_utf16_to_utf8((CHAR16_T*)GetRawUnicode(), GetCount(), s.GetRawUTF8(), length, MINIPAL_MB_NO_REPLACE_INVALID_CHARS)) + s.Resize(length, REPRESENTATION_UTF8); + + //FString::Unicode_Utf8 expects an array all the time + //we optimize the empty string by replacing it with null for SString above in Resize + if (length > 0) { - ThrowHR(EMAKEHR(errno)); + hr = FString::Unicode_Utf8(GetRawUnicode(), allAscii, (LPSTR) s.GetRawUTF8(), length); } } - RETURN (COUNT_T)length + 1; + IfFailThrow(hr); + + RETURN length + 1; } //----------------------------------------------------------------------------- diff --git a/src/coreclr/vm/classcompat.cpp b/src/coreclr/vm/classcompat.cpp index 41fb6735de9daf..b089dad7fdafb6 100644 --- a/src/coreclr/vm/classcompat.cpp +++ b/src/coreclr/vm/classcompat.cpp @@ -2380,7 +2380,7 @@ VOID MethodTableBuilder::EnumerateClassMethods() { BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); } - if (strnlen(strMethodName, MAX_CLASS_NAME) >= MAX_CLASS_NAME) + if (IsStrLongerThan(strMethodName,MAX_CLASS_NAME)) { BuildMethodTableThrowException(BFA_METHOD_NAME_TOO_LONG); } diff --git a/src/coreclr/vm/classhash.cpp b/src/coreclr/vm/classhash.cpp index 03b8c5190c0074..5d2be11c9b3280 100644 --- a/src/coreclr/vm/classhash.cpp +++ b/src/coreclr/vm/classhash.cpp @@ -9,6 +9,7 @@ #include "common.h" #include "classhash.h" #include "dacenumerablehash.inl" +#include "fstring.h" #include "classhash.inl" PTR_EEClassHashEntry EEClassHashEntry::GetEncloser() diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index c271586dc1e7b1..6f984b6483396f 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -26,6 +26,7 @@ #include "shimload.h" #include "debuginfostore.h" +#include "strsafe.h" #include "configuration.h" diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index cecf537129987d..063d8414c59703 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -2751,7 +2751,7 @@ MethodTableBuilder::EnumerateClassMethods() { BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); } - if (strnlen(strMethodName, MAX_CLASS_NAME) >= MAX_CLASS_NAME) + if(IsStrLongerThan(strMethodName,MAX_CLASS_NAME)) { BuildMethodTableThrowException(BFA_METHOD_NAME_TOO_LONG); } @@ -4057,8 +4057,8 @@ VOID MethodTableBuilder::InitializeFieldDescs(FieldDesc *pFieldDescList, BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); } - if (strnlen((char *)pszClassName, MAX_CLASS_NAME) >= MAX_CLASS_NAME - || strnlen((char *)pszNameSpace, MAX_CLASS_NAME) >= MAX_CLASS_NAME + if (IsStrLongerThan((char *)pszClassName, MAX_CLASS_NAME) + || IsStrLongerThan((char *)pszNameSpace, MAX_CLASS_NAME) || (strlen(pszClassName) + strlen(pszNameSpace) + 1 >= MAX_CLASS_NAME)) { BuildMethodTableThrowException(BFA_TYPEREG_NAME_TOO_LONG, mdMethodDefNil); From 8accd8073a44f5835b39e0584f17d1aff1771121 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 21 Jan 2024 21:51:19 -0500 Subject: [PATCH 154/189] Add lots more TensorPrimitives operations (#97192) * Add more TensorPrimitive operations All are functional and tested, some are vectorized, others still need to be. * Re-fix IndexOfXx operations * Rename MultiplyAddEstimate to FusedMultiplyAdd * Remove some NotSupportedException throws * Simplify CopySignOperator * Parameter renames * Add scalar overloads of Atan2 and Atan2Pi * Add CopySign scalar overload * Add Ieee754Remainder scalar overloads * Add Lerp scalar overloads * Add Pow scalar overload * Consolidate inverted operators * Add missing Max/Min scalar overloads * Use ElementWiseSelect --- .../ref/System.Numerics.Tensors.netcore.cs | 96 +- .../Tensors/TensorPrimitives.Single.cs | 8 +- .../TensorPrimitives.Single.netcore.cs | 12 +- .../Tensors/netcore/TensorPrimitives.T.cs | 2639 ++++++++++++++--- .../netcore/TensorPrimitives.netcore.cs | 1942 +++++++++--- .../TensorPrimitives.Single.netstandard.cs | 299 +- .../src/System/ThrowHelper.cs | 6 +- .../tests/TensorPrimitives.Generic.cs | 1452 ++++++++- .../TensorPrimitives.NonGeneric.Single.cs | 208 +- .../tests/TensorPrimitivesTests.cs | 284 +- 10 files changed, 5668 insertions(+), 1278 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs index da348aca973905..031ba109ba6b57 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs @@ -9,48 +9,140 @@ namespace System.Numerics.Tensors public static partial class TensorPrimitives { public static void Abs(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.INumberBase { } + public static void Acosh(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IHyperbolicFunctions { } + public static void AcosPi(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Acos(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } public static void AddMultiply(System.ReadOnlySpan x, System.ReadOnlySpan y, System.ReadOnlySpan multiplier, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IMultiplyOperators { } public static void AddMultiply(System.ReadOnlySpan x, System.ReadOnlySpan y, T multiplier, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IMultiplyOperators { } public static void AddMultiply(System.ReadOnlySpan x, T y, System.ReadOnlySpan multiplier, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IMultiplyOperators { } public static void Add(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity { } public static void Add(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity { } - public static void ConvertToHalf(System.ReadOnlySpan source, System.Span destination) { throw null; } - public static void ConvertToSingle(System.ReadOnlySpan source, System.Span destination) { throw null; } + public static void Asinh(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IHyperbolicFunctions { } + public static void AsinPi(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Asin(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Atan2Pi(System.ReadOnlySpan y, System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Atan2Pi(System.ReadOnlySpan y, T x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Atan2Pi(T y, System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Atan2(System.ReadOnlySpan y, System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Atan2(System.ReadOnlySpan y, T x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Atan2(T y, System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Atanh(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IHyperbolicFunctions { } + public static void AtanPi(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Atan(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void BitwiseAnd(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IBitwiseOperators { } + public static void BitwiseAnd(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IBitwiseOperators { } + public static void BitwiseOr(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IBitwiseOperators { } + public static void BitwiseOr(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IBitwiseOperators { } + public static void Cbrt(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IRootFunctions { } + public static void Ceiling(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void ConvertToHalf(System.ReadOnlySpan source, System.Span destination) { } + public static void ConvertToSingle(System.ReadOnlySpan source, System.Span destination) { } + public static void CopySign(System.ReadOnlySpan x, System.ReadOnlySpan sign, System.Span destination) where T : System.Numerics.INumber { } + public static void CopySign(System.ReadOnlySpan x, T sign, System.Span destination) where T : System.Numerics.INumber { } + public static void CosPi(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Cos(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } public static void Cosh(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IHyperbolicFunctions { } public static T CosineSimilarity(System.ReadOnlySpan x, System.ReadOnlySpan y) where T : System.Numerics.IRootFunctions { throw null; } + public static void DegreesToRadians(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } public static T Distance(System.ReadOnlySpan x, System.ReadOnlySpan y) where T : System.Numerics.IRootFunctions { throw null; } public static void Divide(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IDivisionOperators { } public static void Divide(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IDivisionOperators { } + public static void Divide(T x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IDivisionOperators { } public static T Dot(System.ReadOnlySpan x, System.ReadOnlySpan y) where T : System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IMultiplyOperators, System.Numerics.IMultiplicativeIdentity { throw null; } public static void Exp(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void Exp10M1(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void Exp10(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void Exp2M1(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void Exp2(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void ExpM1(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void Floor(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void Hypot(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IRootFunctions { } + public static void Ieee754Remainder(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Ieee754Remainder(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Ieee754Remainder(T x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void ILogB(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static int IndexOfMaxMagnitude(System.ReadOnlySpan x) where T : System.Numerics.INumber { throw null; } + public static int IndexOfMax(System.ReadOnlySpan x) where T : System.Numerics.INumber { throw null; } + public static int IndexOfMinMagnitude(System.ReadOnlySpan x) where T : System.Numerics.INumber { throw null; } + public static int IndexOfMin(System.ReadOnlySpan x) where T : System.Numerics.INumber { throw null; } + public static void LeadingZeroCount(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void Lerp(System.ReadOnlySpan x, System.ReadOnlySpan y, System.ReadOnlySpan amount, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Lerp(System.ReadOnlySpan x, System.ReadOnlySpan y, T amount, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Lerp(System.ReadOnlySpan x, T y, System.ReadOnlySpan amount, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } public static void Log2(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } + public static void Log2P1(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } + public static void LogP1(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } + public static void Log(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } + public static void Log(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } public static void Log(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } + public static void Log10P1(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } + public static void Log10(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ILogarithmicFunctions { } public static T MaxMagnitude(System.ReadOnlySpan x) where T : System.Numerics.INumberBase { throw null; } public static void MaxMagnitude(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.INumberBase { } + public static void MaxMagnitude(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.INumberBase { } public static T Max(System.ReadOnlySpan x) where T : System.Numerics.INumber { throw null; } public static void Max(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.INumber { } + public static void Max(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.INumber { } public static T MinMagnitude(System.ReadOnlySpan x) where T : System.Numerics.INumberBase { throw null; } public static void MinMagnitude(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.INumberBase { } + public static void MinMagnitude(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.INumberBase { } public static T Min(System.ReadOnlySpan x) where T : System.Numerics.INumber { throw null; } public static void Min(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.INumber { } + public static void Min(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.INumber { } public static void MultiplyAdd(System.ReadOnlySpan x, System.ReadOnlySpan y, System.ReadOnlySpan addend, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IMultiplyOperators { } public static void MultiplyAdd(System.ReadOnlySpan x, System.ReadOnlySpan y, T addend, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IMultiplyOperators { } public static void MultiplyAdd(System.ReadOnlySpan x, T y, System.ReadOnlySpan addend, System.Span destination) where T : System.Numerics.IAdditionOperators, System.Numerics.IMultiplyOperators { } + public static void FusedMultiplyAdd(System.ReadOnlySpan x, System.ReadOnlySpan y, System.ReadOnlySpan addend, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void FusedMultiplyAdd(System.ReadOnlySpan x, System.ReadOnlySpan y, T addend, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void FusedMultiplyAdd(System.ReadOnlySpan x, T y, System.ReadOnlySpan addend, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } public static void Multiply(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IMultiplyOperators, System.Numerics.IMultiplicativeIdentity { } public static void Multiply(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IMultiplyOperators, System.Numerics.IMultiplicativeIdentity { } public static void Negate(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IUnaryNegationOperators { } public static T Norm(System.ReadOnlySpan x) where T : System.Numerics.IRootFunctions { throw null; } + public static void OnesComplement(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IBitwiseOperators { } + public static void PopCount(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void Pow(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IPowerFunctions { } + public static void Pow(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IPowerFunctions { } + public static void Pow(T x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IPowerFunctions { } public static T ProductOfDifferences(System.ReadOnlySpan x, System.ReadOnlySpan y) where T : System.Numerics.ISubtractionOperators, System.Numerics.IMultiplyOperators, System.Numerics.IMultiplicativeIdentity { throw null; } public static T ProductOfSums(System.ReadOnlySpan x, System.ReadOnlySpan y) where T : System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IMultiplyOperators, System.Numerics.IMultiplicativeIdentity { throw null; } public static T Product(System.ReadOnlySpan x) where T : System.Numerics.IMultiplyOperators, System.Numerics.IMultiplicativeIdentity { throw null; } + public static void RadiansToDegrees(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void ReciprocalEstimate(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void ReciprocalSqrtEstimate(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void ReciprocalSqrt(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void Reciprocal(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void RootN(System.ReadOnlySpan x, int n, System.Span destination) where T : System.Numerics.IRootFunctions { } + public static void RotateLeft(System.ReadOnlySpan x, int rotateAmount, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void RotateRight(System.ReadOnlySpan x, int rotateAmount, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void Round(System.ReadOnlySpan x, int digits, System.MidpointRounding mode, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void Round(System.ReadOnlySpan x, int digits, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void Round(System.ReadOnlySpan x, System.MidpointRounding mode, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void Round(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void ScaleB(System.ReadOnlySpan x, int n, System.Span destination) where T : System.Numerics.IFloatingPointIeee754 { } + public static void ShiftLeft(System.ReadOnlySpan x, int shiftAmount, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void ShiftRightArithmetic(System.ReadOnlySpan x, int shiftAmount, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void ShiftRightLogical(System.ReadOnlySpan x, int shiftAmount, System.Span destination) where T : System.Numerics.IBinaryInteger { } public static void Sigmoid(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void SinCosPi(System.ReadOnlySpan x, System.Span sinPiDestination, System.Span cosPiDestination) where T : System.Numerics.ITrigonometricFunctions { } + public static void SinCos(System.ReadOnlySpan x, System.Span sinDestination, System.Span cosDestination) where T : System.Numerics.ITrigonometricFunctions { } public static void Sinh(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IHyperbolicFunctions { } + public static void SinPi(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Sin(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } public static void SoftMax(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IExponentialFunctions { } + public static void Sqrt(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IRootFunctions { } public static void Subtract(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.ISubtractionOperators { } public static void Subtract(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.ISubtractionOperators { } + public static void Subtract(T x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.ISubtractionOperators { } public static T SumOfMagnitudes(System.ReadOnlySpan x) where T : System.Numerics.INumberBase { throw null; } public static T SumOfSquares(System.ReadOnlySpan x) where T : System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IMultiplyOperators { throw null; } public static T Sum(System.ReadOnlySpan x) where T : System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity { throw null; } public static void Tanh(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IHyperbolicFunctions { } + public static void TanPi(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void Tan(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.ITrigonometricFunctions { } + public static void TrailingZeroCount(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IBinaryInteger { } + public static void Truncate(System.ReadOnlySpan x, System.Span destination) where T : System.Numerics.IFloatingPoint { } + public static void Xor(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) where T : System.Numerics.IBitwiseOperators { } + public static void Xor(System.ReadOnlySpan x, T y, System.Span destination) where T : System.Numerics.IBitwiseOperators { } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs index 0ad05d15286d97..7255f7a212bd10 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs @@ -303,7 +303,7 @@ public static void Exp(ReadOnlySpan x, Span destination) => /// /// public static int IndexOfMax(ReadOnlySpan x) => - IndexOfMinMaxCore(x); + IndexOfMinMaxCore(x); /// Searches for the index of the single-precision floating-point number with the largest magnitude in the specified tensor. /// The tensor, represented as a span. @@ -320,7 +320,7 @@ public static int IndexOfMax(ReadOnlySpan x) => /// /// public static int IndexOfMaxMagnitude(ReadOnlySpan x) => - IndexOfMinMaxCore(x); + IndexOfMinMaxCore(x); /// Searches for the index of the smallest single-precision floating-point number in the specified tensor. /// The tensor, represented as a span. @@ -336,7 +336,7 @@ public static int IndexOfMaxMagnitude(ReadOnlySpan x) => /// /// public static int IndexOfMin(ReadOnlySpan x) => - IndexOfMinMaxCore(x); + IndexOfMinMaxCore(x); /// Searches for the index of the single-precision floating-point number with the smallest magnitude in the specified tensor. /// The tensor, represented as a span. @@ -353,7 +353,7 @@ public static int IndexOfMin(ReadOnlySpan x) => /// /// public static int IndexOfMinMagnitude(ReadOnlySpan x) => - IndexOfMinMaxCore(x); + IndexOfMinMaxCore(x); /// Computes the element-wise natural (base e) logarithm of single-precision floating-point numbers in the specified tensor. /// The tensor, represented as a span. diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs index 3747906c5317e6..1148639d48be02 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// This file exists to enable TensorPrimitives.float.cs to be compiled for both +// This file exists to enable TensorPrimitives.Single.cs to be compiled for both // netstandard2.0 and net8.0+ targets. It uses the XX_Single names and the operation // methods tied to float, whereas the net8.0+ worker implementations use generic math. // This file provides float-bound types and type defs that route one to the other. @@ -14,6 +14,10 @@ global using DivideOperator_Single = System.Numerics.Tensors.TensorPrimitives.DivideOperator; global using MultiplyOperator_Single = System.Numerics.Tensors.TensorPrimitives.MultiplyOperator; global using ExpOperator_Single = System.Numerics.Tensors.TensorPrimitives.ExpOperator; +global using IndexOfMaxOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMaxOperator; +global using IndexOfMaxMagnitudeOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMaxMagnitudeOperator; +global using IndexOfMinOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMinOperator; +global using IndexOfMinMagnitudeOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMinMagnitudeOperator; global using LogOperator_Single = System.Numerics.Tensors.TensorPrimitives.LogOperator; global using Log2Operator_Single = System.Numerics.Tensors.TensorPrimitives.Log2Operator; global using MaxOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxOperator; @@ -33,12 +37,6 @@ global using SquaredOperator_Single = System.Numerics.Tensors.TensorPrimitives.SquaredOperator; global using TanhOperator_Single = System.Numerics.Tensors.TensorPrimitives.TanhOperator; -// TODO: These should be made generic. Their implementations are still currently bound to float. -global using IndexOfMaxOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMaxOperator; -global using IndexOfMaxMagnitudeOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMaxMagnitudeOperator; -global using IndexOfMinOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMinOperator; -global using IndexOfMinMagnitudeOperator_Single = System.Numerics.Tensors.TensorPrimitives.IndexOfMinMagnitudeOperator; - namespace System.Numerics.Tensors { public static unsafe partial class TensorPrimitives diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs index f9a44e8680123d..fdf8e37fb06e58 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs @@ -1,8 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// TODO: -// - Provide generic overloads for the IndexOfMin/Max{Magnitude} methods +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + namespace System.Numerics.Tensors { /// Performs primitive tensor operations over spans of memory. @@ -16,7 +17,7 @@ public static partial class TensorPrimitives /// is a signed integer type and contained a value equal to 's minimum value. /// /// - /// This method effectively computes [i] = MathF.Abs([i]). + /// This method effectively computes [i] = .Abs([i]). /// /// /// The absolute value of a is its numeric value without its sign. For example, the absolute value of both 1.2e-03 and -1.2e03 is 1.2e03. @@ -30,419 +31,287 @@ public static void Abs(ReadOnlySpan x, Span destination) where T : INumberBase => InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise addition of numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. + /// Computes the element-wise angle in radians whose cosine is the specifed number. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = [i] + [i]. + /// This method effectively computes [i] = .Acos([i]). /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void Add(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : IAdditionOperators, IAdditiveIdentity => - InvokeSpanSpanIntoSpan>(x, y, destination); + public static void Acos(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise addition of numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a scalar. + /// Computes the element-wise hyperbolic arc-cosine of the specifed number. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = [i] + . + /// This method effectively computes [i] = .Acosh([i]). /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void Add(ReadOnlySpan x, T y, Span destination) - where T : IAdditionOperators, IAdditiveIdentity => - InvokeSpanScalarIntoSpan>(x, y, destination); + public static void Acosh(ReadOnlySpan x, Span destination) + where T : IHyperbolicFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise result of ( + ) * for the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The third tensor, represented as a span. + /// Computes the element-wise angle in radians whose cosine is the specifed number and divides the result by Pi. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of and the length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = ([i] + [i]) * [i]. + /// This method effectively computes [i] = .AcosPi([i]). /// /// - /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan multiplier, Span destination) - where T : IAdditionOperators, IMultiplyOperators => - InvokeSpanSpanSpanIntoSpan>(x, y, multiplier, destination); + public static void AcosPi(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise result of ( + ) * for the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The third tensor, represented as a scalar. + /// Computes the element-wise angle in radians whose sine is the specifed number. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = ([i] + [i]) * . + /// This method effectively computes [i] = .Asin([i]). /// /// - /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, T multiplier, Span destination) - where T : IAdditionOperators, IMultiplyOperators => - InvokeSpanSpanScalarIntoSpan>(x, y, multiplier, destination); + public static void Asin(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise result of ( + ) * for the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a scalar. - /// The third tensor, represented as a span. + /// Computes the element-wise hyperbolic arc-sine of the specifed number. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = ([i] + ) * [i]. + /// This method effectively computes [i] = .Asinh([i]). /// /// - /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void AddMultiply(ReadOnlySpan x, T y, ReadOnlySpan multiplier, Span destination) - where T : IAdditionOperators, IMultiplyOperators => - InvokeSpanScalarSpanIntoSpan>(x, y, multiplier, destination); + public static void Asinh(ReadOnlySpan x, Span destination) + where T : IHyperbolicFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise hyperbolic cosine of each radian angle in the specified tensor. + /// Computes the element-wise angle in radians whose sine is the specifed number and divides the result by Pi. /// The tensor, represented as a span. /// The destination tensor, represented as a span. /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = .Cosh([i]). - /// - /// - /// If a value is equal to or , the result stored into the corresponding destination location is set to . - /// If a value is equal to , the result stored into the corresponding destination location is also NaN. - /// - /// - /// The angles in x must be in radians. Use or multiply by /180 to convert degrees to radians. + /// This method effectively computes [i] = .AsinPi([i]). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static void Cosh(ReadOnlySpan x, Span destination) - where T : IHyperbolicFunctions => - InvokeSpanIntoSpan>(x, destination); + public static void AsinPi(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the cosine similarity between the two specified non-empty, equal-length tensors of numbers. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The cosine similarity of the two tensors. - /// Length of must be same as length of . - /// and must not be empty. + /// Computes the element-wise angle in radians whose tangent is the specifed number. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes TensorPrimitives.Dot(x, y) / (MathF.Sqrt(TensorPrimitives.SumOfSquares(x)) * MathF.Sqrt(TensorPrimitives.SumOfSquares(y)). - /// - /// - /// If any element in either input tensor is equal to , , or , - /// NaN is returned. + /// This method effectively computes [i] = .Atan([i]). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static T CosineSimilarity(ReadOnlySpan x, ReadOnlySpan y) - where T : IRootFunctions => - CosineSimilarityCore(x, y); + public static void Atan(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the distance between two points, specified as non-empty, equal-length tensors of numbers, in Euclidean space. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The Euclidean distance. - /// Length of must be same as length of . - /// and must not be empty. + /// Computes the element-wise hyperbolic arc-tangent of the specifed number. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes the equivalent of: - /// - /// Span<T> difference = ...; - /// TensorPrimitives.Subtract(x, y, difference); - /// T result = MathF.Sqrt(TensorPrimitives.SumOfSquares(difference)); - /// - /// but without requiring additional temporary storage for the intermediate differences. - /// - /// - /// If any element in either input tensor is equal to , NaN is returned. + /// This method effectively computes [i] = .Atanh([i]). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static T Distance(ReadOnlySpan x, ReadOnlySpan y) - where T : IRootFunctions - { - if (x.IsEmpty) - { - ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); - } - - return T.Sqrt(Aggregate, AddOperator>(x, y)); - } + public static void Atanh(ReadOnlySpan x, Span destination) + where T : IHyperbolicFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise division of numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. + /// Computes the element-wise angle in radians whose tangent is the specifed number and divides the result by Pi. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. - /// is an integer type and an element in is equal to zero. /// /// - /// This method effectively computes [i] = [i] / [i]. + /// This method effectively computes [i] = .AtanPi([i]). /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void Divide(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : IDivisionOperators => - InvokeSpanSpanIntoSpan>(x, y, destination); + public static void AtanPi(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise division of numbers in the specified tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a scalar. + /// Computes the element-wise arc-tangent for the quotient of two values in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. /// The destination tensor, represented as a span. + /// Length of must be same as length of . /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// and reference overlapping memory locations and do not begin at the same location. - /// is an integer type and is equal to zero. /// /// - /// This method effectively computes [i] = [i] / . + /// This method effectively computes [i] = .Atan2([i], [i]). /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void Divide(ReadOnlySpan x, T y, Span destination) - where T : IDivisionOperators => - InvokeSpanScalarIntoSpan>(x, y, destination); + public static void Atan2(ReadOnlySpan y, ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanIntoSpan>(y, x, destination); - /// Computes the dot product of two tensors containing numbers. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The dot product. - /// Length of must be same as length of . + /// Computes the element-wise arc-tangent for the quotient of two values in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes the equivalent of: - /// - /// Span<T> products = ...; - /// TensorPrimitives.Multiply(x, y, products); - /// T result = TensorPrimitives.Sum(products); - /// - /// but without requiring additional temporary storage for the intermediate products. It corresponds to the dot method defined by BLAS1. - /// - /// - /// If any of the input elements is equal to , the resulting value is also NaN. + /// This method effectively computes [i] = .Atan2([i], ). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static T Dot(ReadOnlySpan x, ReadOnlySpan y) - where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators, IMultiplicativeIdentity => - Aggregate, AddOperator>(x, y); + public static void Atan2(ReadOnlySpan y, T x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanScalarIntoSpan>(y, x, destination); - /// Computes the element-wise result of raising e to the number powers in the specified tensor. - /// The tensor, represented as a span. + /// Computes the element-wise arc-tangent for the quotient of two values in the specified tensors. + /// The first tensor, represented as a scalar. + /// The second tensor, represented as a span. /// The destination tensor, represented as a span. /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = .Exp([i]). - /// - /// - /// If a value equals or , the result stored into the corresponding destination location is set to NaN. - /// If a value equals , the result stored into the corresponding destination location is set to 0. + /// This method effectively computes [i] = .Atan2(, [i]). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static void Exp(ReadOnlySpan x, Span destination) - where T : IExponentialFunctions => - InvokeSpanIntoSpan>(x, destination); - - // TODO: Make IndexOfXx methods generic - // - ///// Searches for the index of the largest number in the specified tensor. - ///// The tensor, represented as a span. - ///// The index of the maximum element in , or -1 if is empty. - ///// - ///// - ///// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If any value equal to - ///// is present, the index of the first is returned. Positive 0 is considered greater than negative 0. - ///// - ///// - ///// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - ///// operating systems or architectures. - ///// - ///// - //public static int IndexOfMax(ReadOnlySpan x) - // where T : INumber => - // IndexOfMinMaxCore>(x); - // - ///// Searches for the index of the number with the largest magnitude in the specified tensor. - ///// The tensor, represented as a span. - ///// The index of the element in with the largest magnitude (absolute value), or -1 if is empty. - ///// - ///// - ///// The determination of the maximum magnitude matches the IEEE 754:2019 `maximumMagnitude` function. If any value equal to - ///// is present, the index of the first is returned. If two values have the same magnitude and one is positive and the other is negative, - ///// the positive value is considered to have the larger magnitude. - ///// - ///// - ///// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - ///// operating systems or architectures. - ///// - ///// - //public static int IndexOfMaxMagnitude(ReadOnlySpan x) - // where T : INumberBase => - // IndexOfMinMaxCore>(x); - // - ///// Searches for the index of the smallest number in the specified tensor. - ///// The tensor, represented as a span. - ///// The index of the minimum element in , or -1 if is empty. - ///// - ///// - ///// The determination of the minimum element matches the IEEE 754:2019 `minimum` function. If any value equal to - ///// is present, the index of the first is returned. Negative 0 is considered smaller than positive 0. - ///// - ///// - ///// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - ///// operating systems or architectures. - ///// - ///// - //public static int IndexOfMin(ReadOnlySpan x) - // where T : INumber => - // IndexOfMinMaxCore>(x); - // - ///// Searches for the index of the number with the smallest magnitude in the specified tensor. - ///// The tensor, represented as a span. - ///// The index of the element in with the smallest magnitude (absolute value), or -1 if is empty. - ///// - ///// - ///// The determination of the minimum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If any value equal to - ///// is present, the index of the first is returned. If two values have the same magnitude and one is positive and the other is negative, - ///// the negative value is considered to have the smaller magnitude. - ///// - ///// - ///// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - ///// operating systems or architectures. - ///// - ///// - //public static int IndexOfMinMagnitude(ReadOnlySpan x) - // where T : INumberBase => - // IndexOfMinMaxCore>(x); + public static void Atan2(T y, ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeScalarSpanIntoSpan>(y, x, destination); - /// Computes the element-wise natural (base e) logarithm of numbers in the specified tensor. - /// The tensor, represented as a span. + /// Computes the element-wise arc-tangent for the quotient of two values in the specified tensors and divides the result by Pi. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. /// The destination tensor, represented as a span. + /// Length of must be same as length of . /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = .Log([i]). - /// - /// - /// If a value equals 0, the result stored into the corresponding destination location is set to . - /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. - /// If a value is positive infinity, the result stored into the corresponding destination location is set to . - /// Otherwise, if a value is positive, its natural logarithm is stored into the corresponding destination location. + /// This method effectively computes [i] = .Atan2([i], [i]). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static void Log(ReadOnlySpan x, Span destination) - where T : ILogarithmicFunctions => - InvokeSpanIntoSpan>(x, destination); + public static void Atan2Pi(ReadOnlySpan y, ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanIntoSpan>(y, x, destination); - /// Computes the element-wise base 2 logarithm of numbers in the specified tensor. - /// The tensor, represented as a span. + /// Computes the element-wise arc-tangent for the quotient of two values in the specified tensors and divides the result by Pi. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. /// The destination tensor, represented as a span. /// Destination is too short. - /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = .Log2([i]). - /// - /// - /// If a value equals 0, the result stored into the corresponding destination location is set to . - /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. - /// If a value is positive infinity, the result stored into the corresponding destination location is set to . - /// Otherwise, if a value is positive, its natural logarithm is stored into the corresponding destination location. + /// This method effectively computes [i] = .Atan2([i], ). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static void Log2(ReadOnlySpan x, Span destination) - where T : ILogarithmicFunctions => - InvokeSpanIntoSpan>(x, destination); + public static void Atan2Pi(ReadOnlySpan y, T x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanScalarIntoSpan>(y, x, destination); - /// Searches for the largest number in the specified tensor. - /// The tensor, represented as a span. - /// The maximum element in . - /// Length of must be greater than zero. + /// Computes the element-wise arc-tangent for the quotient of two values in the specified tensors and divides the result by Pi. + /// The first tensor, represented as a scalar. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If any value equal to - /// is present, the first is returned. Positive 0 is considered greater than negative 0. + /// This method effectively computes [i] = .Atan2(, [i]). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static T Max(ReadOnlySpan x) - where T : INumber => - MinMaxCore>(x); + public static void Atan2Pi(T y, ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeScalarSpanIntoSpan>(y, x, destination); - /// Computes the element-wise maximum of the numbers in the specified tensors. + /// Computes the element-wise addition of numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. @@ -452,80 +321,60 @@ public static T Max(ReadOnlySpan x) /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = MathF.Max([i], [i]). - /// - /// - /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If either value is equal to , - /// that value is stored as the result. Positive 0 is considered greater than negative 0. + /// This method effectively computes [i] = [i] + [i]. /// /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. /// /// - public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : INumber => - InvokeSpanSpanIntoSpan>(x, y, destination); + public static void Add(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IAdditionOperators, IAdditiveIdentity => + InvokeSpanSpanIntoSpan>(x, y, destination); - /// Searches for the number with the largest magnitude in the specified tensor. - /// The tensor, represented as a span. - /// The element in with the largest magnitude (absolute value). - /// Length of must be greater than zero. + /// Computes the element-wise addition of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// The determination of the maximum magnitude matches the IEEE 754:2019 `maximumMagnitude` function. If any value equal to - /// is present, the first is returned. If two values have the same magnitude and one is positive and the other is negative, - /// the positive value is considered to have the larger magnitude. + /// This method effectively computes [i] = [i] + . /// /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. /// /// - public static T MaxMagnitude(ReadOnlySpan x) - where T : INumberBase => - MinMaxCore>(x); + public static void Add(ReadOnlySpan x, T y, Span destination) + where T : IAdditionOperators, IAdditiveIdentity => + InvokeSpanScalarIntoSpan>(x, y, destination); - /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// Computes the element-wise result of ( + ) * for the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. + /// The third tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . + /// Length of must be same as length of and the length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. /// and reference overlapping memory locations and do not begin at the same location. - /// This method effectively computes [i] = MathF.MaxMagnitude([i], [i]). - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. - /// - /// - public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : INumberBase => - InvokeSpanSpanIntoSpan>(x, y, destination); - - /// Searches for the smallest number in the specified tensor. - /// The tensor, represented as a span. - /// The minimum element in . - /// Length of must be greater than zero. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// The determination of the minimum element matches the IEEE 754:2019 `minimum` function. If any value is equal to - /// is present, the first is returned. Negative 0 is considered smaller than positive 0. + /// This method effectively computes [i] = ([i] + [i]) * [i]. /// /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. /// /// - public static T Min(ReadOnlySpan x) - where T : INumber => - MinMaxCore>(x); + public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan multiplier, Span destination) + where T : IAdditionOperators, IMultiplyOperators => + InvokeSpanSpanSpanIntoSpan>(x, y, multiplier, destination); - /// Computes the element-wise minimum of the numbers in the specified tensors. + /// Computes the element-wise result of ( + ) * for the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. + /// The third tensor, represented as a scalar. /// The destination tensor, represented as a span. /// Length of must be same as length of . /// Destination is too short. @@ -533,41 +382,38 @@ public static T Min(ReadOnlySpan x) /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = MathF.Max([i], [i]). - /// - /// - /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If either value is equal to , - /// that value is stored as the result. Positive 0 is considered greater than negative 0. + /// This method effectively computes [i] = ([i] + [i]) * . /// /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. /// /// - public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : INumber => - InvokeSpanSpanIntoSpan>(x, y, destination); + public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, T multiplier, Span destination) + where T : IAdditionOperators, IMultiplyOperators => + InvokeSpanSpanScalarIntoSpan>(x, y, multiplier, destination); - /// Searches for the number with the smallest magnitude in the specified tensor. - /// The tensor, represented as a span. - /// The element in with the smallest magnitude (absolute value). - /// Length of must be greater than zero. + /// Computes the element-wise result of ( + ) * for the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// The determination of the minimum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If any value equal to - /// is present, the first is returned. If two values have the same magnitude and one is positive and the other is negative, - /// the negative value is considered to have the smaller magnitude. + /// This method effectively computes [i] = ([i] + ) * [i]. /// /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. /// /// - public static T MinMagnitude(ReadOnlySpan x) - where T : INumberBase => - MinMaxCore>(x); + public static void AddMultiply(ReadOnlySpan x, T y, ReadOnlySpan multiplier, Span destination) + where T : IAdditionOperators, IMultiplyOperators => + InvokeSpanScalarSpanIntoSpan>(x, y, multiplier, destination); - /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// Computes the element-wise bitwise AND of numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. @@ -575,23 +421,31 @@ public static T MinMagnitude(ReadOnlySpan x) /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. /// and reference overlapping memory locations and do not begin at the same location. - /// This method effectively computes [i] = MathF.MinMagnitude([i], [i]). /// /// - /// The determination of the maximum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If either value is equal to , - /// that value is stored as the result. If the two values have the same magnitude and one is positive and the other is negative, - /// the negative value is considered to have the smaller magnitude. + /// This method effectively computes [i] = [i] & [i]. /// + /// + public static void BitwiseAnd(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IBitwiseOperators => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise bitwise AND of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// This method effectively computes [i] = [i] & . /// /// - public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : INumberBase => - InvokeSpanSpanIntoSpan>(x, y, destination); + public static void BitwiseAnd(ReadOnlySpan x, T y, Span destination) + where T : IBitwiseOperators => + InvokeSpanScalarIntoSpan>(x, y, destination); - /// Computes the element-wise product of numbers in the specified tensors. + /// Computes the element-wise bitwise OR of numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. /// The destination tensor, represented as a span. @@ -601,17 +455,14 @@ public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = [i] * [i]. - /// - /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method effectively computes [i] = [i] | [i]. /// /// - public static void Multiply(ReadOnlySpan x, ReadOnlySpan y, Span destination) - where T : IMultiplyOperators, IMultiplicativeIdentity => - InvokeSpanSpanIntoSpan>(x, y, destination); + public static void BitwiseOr(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IBitwiseOperators => + InvokeSpanSpanIntoSpan>(x, y, destination); - /// Computes the element-wise product of numbers in the specified tensors. + /// Computes the element-wise bitwise OR of numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a scalar. /// The destination tensor, represented as a span. @@ -619,206 +470,1808 @@ public static void Multiply(ReadOnlySpan x, ReadOnlySpan y, Span des /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = [i] * . - /// It corresponds to the scal method defined by BLAS1. + /// This method effectively computes [i] = [i] | . /// + /// + public static void BitwiseOr(ReadOnlySpan x, T y, Span destination) + where T : IBitwiseOperators => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Computes the element-wise ceiling of numbers in the specified tensor. + /// The first tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method effectively computes [i] = T.Ceiling([i]). /// /// - public static void Multiply(ReadOnlySpan x, T y, Span destination) - where T : IMultiplyOperators, IMultiplicativeIdentity => - InvokeSpanScalarIntoSpan>(x, y, destination); + public static void Ceiling(ReadOnlySpan x, Span destination) + where T : IFloatingPoint => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors. /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The third tensor, represented as a span. + /// The second tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of and length of . + /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = ([i] * [i]) + [i]. + /// This method effectively computes [i] = T.CopySign([i], [i]). /// + /// + public static void CopySign(ReadOnlySpan x, ReadOnlySpan sign, Span destination) + where T : INumber => + InvokeSpanSpanIntoSpan>(x, sign, destination); + + /// Computes the element-wise result of copying the sign from one number to another number in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// This method effectively computes [i] = T.CopySign([i], [i]). /// /// - public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan addend, Span destination) - where T : IAdditionOperators, IMultiplyOperators => - InvokeSpanSpanSpanIntoSpan>(x, y, addend, destination); + public static void CopySign(ReadOnlySpan x, T sign, Span destination) + where T : INumber => + InvokeSpanScalarIntoSpan>(x, sign, destination); - /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The third tensor, represented as a scalar. + /// Computes the element-wise cosine of the value in the specified tensor. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = ([i] * [i]) + . - /// It corresponds to the axpy method defined by BLAS1. + /// This method effectively computes [i] = .Cos([i]). /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, T addend, Span destination) - where T : IAdditionOperators, IMultiplyOperators => - InvokeSpanSpanScalarIntoSpan>(x, y, addend, destination); + public static void Cos(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. - /// The first tensor, represented as a span. - /// The second tensor, represented as a scalar. - /// The third tensor, represented as a span. + /// Computes the element-wise cosine of the value in the specified tensor that has been multiplied by Pi. + /// The tensor, represented as a span. /// The destination tensor, represented as a span. - /// Length of must be same as length of . /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. - /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = ([i] * ) + [i]. + /// This method effectively computes [i] = .CosPi([i]). /// /// - /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void MultiplyAdd(ReadOnlySpan x, T y, ReadOnlySpan addend, Span destination) - where T : IAdditionOperators, IMultiplyOperators => - InvokeSpanScalarSpanIntoSpan>(x, y, addend, destination); + public static void CosPi(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise negation of each number in the specified tensor. + /// Computes the element-wise hyperbolic cosine of each radian angle in the specified tensor. /// The tensor, represented as a span. /// The destination tensor, represented as a span. /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = -[i]. + /// This method effectively computes [i] = .Cosh([i]). /// /// - /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// If a value is equal to or , the result stored into the corresponding destination location is set to . + /// If a value is equal to , the result stored into the corresponding destination location is also NaN. + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. /// /// - public static void Negate(ReadOnlySpan x, Span destination) - where T : IUnaryNegationOperators => - InvokeSpanIntoSpan>(x, destination); + public static void Cosh(ReadOnlySpan x, Span destination) + where T : IHyperbolicFunctions => + InvokeSpanIntoSpan>(x, destination); - /// Computes the Euclidean norm of the specified tensor of numbers. + /// Computes the cosine similarity between the two specified non-empty, equal-length tensors of numbers. /// The first tensor, represented as a span. - /// The norm. + /// The second tensor, represented as a span. + /// The cosine similarity of the two tensors. + /// Length of must be same as length of . + /// and must not be empty. /// /// - /// This method effectively computes MathF.Sqrt(TensorPrimitives.SumOfSquares(x)). - /// This is often referred to as the Euclidean norm or L2 norm. - /// It corresponds to the nrm2 method defined by BLAS1. + /// This method effectively computes TensorPrimitives.Dot(x, y) / (.Sqrt(TensorPrimitives.SumOfSquares(x)) * .Sqrt(TensorPrimitives.SumOfSquares(y)). /// /// - /// If any of the input values is equal to , the result value is also NaN. + /// If any element in either input tensor is equal to , , or , + /// NaN is returned. /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different /// operating systems or architectures. /// /// - public static T Norm(ReadOnlySpan x) + public static T CosineSimilarity(ReadOnlySpan x, ReadOnlySpan y) where T : IRootFunctions => - T.Sqrt(SumOfSquares(x)); + CosineSimilarityCore(x, y); + + /// Computes the element-wise cube root of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Cbrt([i]). + /// + /// + public static void Cbrt(ReadOnlySpan x, Span destination) + where T : IRootFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise conversion of each number of degrees in the specified tensor to radiansx. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .DegreesToRadians([i]). + /// + /// + public static void DegreesToRadians(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the distance between two points, specified as non-empty, equal-length tensors of numbers, in Euclidean space. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The Euclidean distance. + /// Length of must be same as length of . + /// and must not be empty. + /// + /// + /// This method effectively computes the equivalent of: + /// + /// Span<T> difference = ...; + /// TensorPrimitives.Subtract(x, y, difference); + /// T result = .Sqrt(TensorPrimitives.SumOfSquares(difference)); + /// + /// but without requiring additional temporary storage for the intermediate differences. + /// + /// + /// If any element in either input tensor is equal to , NaN is returned. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Distance(ReadOnlySpan x, ReadOnlySpan y) + where T : IRootFunctions + { + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + return T.Sqrt(Aggregate, AddOperator>(x, y)); + } + + /// Computes the element-wise division of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and an element in is equal to zero. + /// + /// + /// This method effectively computes [i] = [i] / [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Divide(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IDivisionOperators => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise division of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and is equal to zero. + /// + /// + /// This method effectively computes [i] = [i] / . + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Divide(ReadOnlySpan x, T y, Span destination) + where T : IDivisionOperators => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Computes the element-wise division of numbers in the specified tensors. + /// The first tensor, represented as a scalar. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and an element in is equal to zero. + /// + /// + /// This method effectively computes [i] = / [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Divide(T x, ReadOnlySpan y, Span destination) + where T : IDivisionOperators => + InvokeScalarSpanIntoSpan>(x, y, destination); + + /// Computes the dot product of two tensors containing numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The dot product. + /// Length of must be same as length of . + /// + /// + /// This method effectively computes the equivalent of: + /// + /// Span<T> products = ...; + /// TensorPrimitives.Multiply(x, y, products); + /// T result = TensorPrimitives.Sum(products); + /// + /// but without requiring additional temporary storage for the intermediate products. It corresponds to the dot method defined by BLAS1. + /// + /// + /// If any of the input elements is equal to , the resulting value is also NaN. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Dot(ReadOnlySpan x, ReadOnlySpan y) + where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators, IMultiplicativeIdentity => + Aggregate, AddOperator>(x, y); + + /// Computes the element-wise result of raising e to the number powers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Exp([i]). + /// + /// + /// If a value equals or , the result stored into the corresponding destination location is set to NaN. + /// If a value equals , the result stored into the corresponding destination location is set to 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Exp(ReadOnlySpan x, Span destination) + where T : IExponentialFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise result of raising e to the number powers in the specified tensor, minus 1. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .ExpM1([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void ExpM1(ReadOnlySpan x, Span destination) + where T : IExponentialFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise result of raising 2 to the number powers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Exp2([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Exp2(ReadOnlySpan x, Span destination) + where T : IExponentialFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise result of raising 2 to the number powers in the specified tensor, minus one. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Exp2M1([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Exp2M1(ReadOnlySpan x, Span destination) + where T : IExponentialFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise result of raising 10 to the number powers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Exp10([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Exp10(ReadOnlySpan x, Span destination) + where T : IExponentialFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise result of raising 10 to the number powers in the specified tensor, minus one. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Exp10M1([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Exp10M1(ReadOnlySpan x, Span destination) + where T : IExponentialFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise floor of numbers in the specified tensor. + /// The first tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Floor([i]). + /// + /// + public static void Floor(ReadOnlySpan x, Span destination) + where T : IFloatingPoint => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise hypotensue given values from two tensors representing the lengths of the shorter sides in a right-angled triangle. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Hypot([i], [i]). + /// + /// + public static void Hypot(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IRootFunctions => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Ieee754Remainder([i], [i]). + /// + /// + public static void Ieee754Remainder(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Ieee754Remainder([i], ). + /// + /// + public static void Ieee754Remainder(ReadOnlySpan x, T y, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Computes the element-wise remainder of the numbers in the specified tensors. + /// The first tensor, represented as a scalar. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Ieee754Remainder(, [i]). + /// + /// + public static void Ieee754Remainder(T x, ReadOnlySpan y, Span destination) + where T : IFloatingPointIeee754 => + InvokeScalarSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise integer logarithm of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.ILogB([i]). + /// + /// + public static void ILogB(ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.ILogB(x[i]); + } + } + + /// Searches for the index of the largest number in the specified tensor. + /// The tensor, represented as a span. + /// The index of the maximum element in , or -1 if is empty. + /// + /// + /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If any value equal to NaN + /// is present, the index of the first is returned. Positive 0 is considered greater than negative 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static int IndexOfMax(ReadOnlySpan x) + where T : INumber => + IndexOfMinMaxCore>(x); + + /// Searches for the index of the number with the largest magnitude in the specified tensor. + /// The tensor, represented as a span. + /// The index of the element in with the largest magnitude (absolute value), or -1 if is empty. + /// + /// + /// The determination of the maximum magnitude matches the IEEE 754:2019 `maximumMagnitude` function. If any value equal to NaN + /// is present, the index of the first is returned. If two values have the same magnitude and one is positive and the other is negative, + /// the positive value is considered to have the larger magnitude. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static int IndexOfMaxMagnitude(ReadOnlySpan x) + where T : INumber => + IndexOfMinMaxCore>(x); + + /// Searches for the index of the smallest number in the specified tensor. + /// The tensor, represented as a span. + /// The index of the minimum element in , or -1 if is empty. + /// + /// + /// The determination of the minimum element matches the IEEE 754:2019 `minimum` function. If any value equal to NaN + /// is present, the index of the first is returned. Negative 0 is considered smaller than positive 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static int IndexOfMin(ReadOnlySpan x) + where T : INumber => + IndexOfMinMaxCore>(x); + + /// Searches for the index of the number with the smallest magnitude in the specified tensor. + /// The tensor, represented as a span. + /// The index of the element in with the smallest magnitude (absolute value), or -1 if is empty. + /// + /// + /// The determination of the minimum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If any value equal to NaN + /// is present, the index of the first is returned. If two values have the same magnitude and one is positive and the other is negative, + /// the negative value is considered to have the smaller magnitude. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static int IndexOfMinMagnitude(ReadOnlySpan x) + where T : INumber => + IndexOfMinMaxCore>(x); + + /// Computes the element-wise leading zero count of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.LeadingZeroCount([i]). + /// + /// + public static void LeadingZeroCount(ReadOnlySpan x, Span destination) + where T : IBinaryInteger => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise linear interpolation between two values based on the given weight in the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of and length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Lerp([i], [i], [i]). + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Lerp(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan amount, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanSpanIntoSpan>(x, y, amount, destination); + + /// Computes the element-wise linear interpolation between two values based on the given weight in the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The third tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Lerp([i], [i], ). + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Lerp(ReadOnlySpan x, ReadOnlySpan y, T amount, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanScalarIntoSpan>(x, y, amount, destination); + + /// Computes the element-wise linear interpolation between two values based on the given weight in the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Lerp([i], , [i]). + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Lerp(ReadOnlySpan x, T y, ReadOnlySpan amount, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanScalarSpanIntoSpan>(x, y, amount, destination); + + /// Computes the element-wise natural (base e) logarithm of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log([i]). + /// + /// + /// If a value equals 0, the result stored into the corresponding destination location is set to . + /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. + /// If a value is positive infinity, the result stored into the corresponding destination location is set to . + /// Otherwise, if a value is positive, its natural logarithm is stored into the corresponding destination location. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log(ReadOnlySpan x, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise base 2 logarithm of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log2([i]). + /// + /// + /// If a value equals 0, the result stored into the corresponding destination location is set to . + /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. + /// If a value is positive infinity, the result stored into the corresponding destination location is set to . + /// Otherwise, if a value is positive, its base 2 logarithm is stored into the corresponding destination location. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log2(ReadOnlySpan x, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise base 10 logarithm of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log10([i]). + /// + /// + /// If a value equals 0, the result stored into the corresponding destination location is set to . + /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. + /// If a value is positive infinity, the result stored into the corresponding destination location is set to . + /// Otherwise, if a value is positive, its base 10 logarithm is stored into the corresponding destination location. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log10(ReadOnlySpan x, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise natural (base e) logarithm of numbers in the specified tensor plus 1. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .LogP1([i]). + /// + /// + /// If a value equals 0, the result stored into the corresponding destination location is set to . + /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. + /// If a value is positive infinity, the result stored into the corresponding destination location is set to . + /// Otherwise, if a value is positive, its natural logarithm plus 1 is stored into the corresponding destination location. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void LogP1(ReadOnlySpan x, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise base 2 logarithm of numbers in the specified tensor plus 1. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log2P1([i]). + /// + /// + /// If a value equals 0, the result stored into the corresponding destination location is set to . + /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. + /// If a value is positive infinity, the result stored into the corresponding destination location is set to . + /// Otherwise, if a value is positive, its base 2 logarithm plus 1 is stored into the corresponding destination location. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log2P1(ReadOnlySpan x, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise base 10 logarithm of numbers in the specified tensor plus 1. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log10P1([i]). + /// + /// + /// If a value equals 0, the result stored into the corresponding destination location is set to . + /// If a value is negative or equal to , the result stored into the corresponding destination location is set to NaN. + /// If a value is positive infinity, the result stored into the corresponding destination location is set to . + /// Otherwise, if a value is positive, its base 10 logarithm plus 1 is stored into the corresponding destination location. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log10P1(ReadOnlySpan x, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log([i], [i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise logarithm of the numbers in a specified tensor to the specified base in another specified tensor. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Log([i], ). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Log(ReadOnlySpan x, T y, Span destination) + where T : ILogarithmicFunctions => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Searches for the largest number in the specified tensor. + /// The tensor, represented as a span. + /// The maximum element in . + /// Length of must be greater than zero. + /// + /// + /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If any value equal to + /// is present, the first is returned. Positive 0 is considered greater than negative 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Max(ReadOnlySpan x) + where T : INumber => + MinMaxCore>(x); + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Max([i], [i]). + /// + /// + /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If either value is equal to , + /// that value is stored as the result. Positive 0 is considered greater than negative 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : INumber => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise maximum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Max([i], ). + /// + /// + /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If either value is equal to , + /// that value is stored as the result. Positive 0 is considered greater than negative 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Max(ReadOnlySpan x, T y, Span destination) + where T : INumber => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Searches for the number with the largest magnitude in the specified tensor. + /// The tensor, represented as a span. + /// The element in with the largest magnitude (absolute value). + /// Length of must be greater than zero. + /// + /// + /// The determination of the maximum magnitude matches the IEEE 754:2019 `maximumMagnitude` function. If any value equal to + /// is present, the first is returned. If two values have the same magnitude and one is positive and the other is negative, + /// the positive value is considered to have the larger magnitude. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T MaxMagnitude(ReadOnlySpan x) + where T : INumberBase => + MinMaxCore>(x); + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// This method effectively computes [i] = .MaxMagnitude([i], [i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : INumberBase => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise number with the largest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// This method effectively computes [i] = .MaxMagnitude([i], ). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void MaxMagnitude(ReadOnlySpan x, T y, Span destination) + where T : INumberBase => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Searches for the smallest number in the specified tensor. + /// The tensor, represented as a span. + /// The minimum element in . + /// Length of must be greater than zero. + /// + /// + /// The determination of the minimum element matches the IEEE 754:2019 `minimum` function. If any value is equal to + /// is present, the first is returned. Negative 0 is considered smaller than positive 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Min(ReadOnlySpan x) + where T : INumber => + MinMaxCore>(x); + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Max([i], [i]). + /// + /// + /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If either value is equal to , + /// that value is stored as the result. Positive 0 is considered greater than negative 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : INumber => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise minimum of the numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Max([i], ). + /// + /// + /// The determination of the maximum element matches the IEEE 754:2019 `maximum` function. If either value is equal to , + /// that value is stored as the result. Positive 0 is considered greater than negative 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Min(ReadOnlySpan x, T y, Span destination) + where T : INumber => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Searches for the number with the smallest magnitude in the specified tensor. + /// The tensor, represented as a span. + /// The element in with the smallest magnitude (absolute value). + /// Length of must be greater than zero. + /// + /// + /// The determination of the minimum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If any value equal to + /// is present, the first is returned. If two values have the same magnitude and one is positive and the other is negative, + /// the negative value is considered to have the smaller magnitude. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T MinMagnitude(ReadOnlySpan x) + where T : INumberBase => + MinMaxCore>(x); + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// This method effectively computes [i] = .MinMagnitude([i], [i]). + /// + /// + /// The determination of the maximum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If either value is equal to , + /// that value is stored as the result. If the two values have the same magnitude and one is positive and the other is negative, + /// the negative value is considered to have the smaller magnitude. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : INumberBase => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise number with the smallest magnitude in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// This method effectively computes [i] = .MinMagnitude([i], ). + /// + /// + /// The determination of the maximum magnitude matches the IEEE 754:2019 `minimumMagnitude` function. If either value is equal to , + /// that value is stored as the result. If the two values have the same magnitude and one is positive and the other is negative, + /// the negative value is considered to have the smaller magnitude. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void MinMagnitude(ReadOnlySpan x, T y, Span destination) + where T : INumberBase => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Computes the element-wise product of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = [i] * [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Multiply(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IMultiplyOperators, IMultiplicativeIdentity => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise product of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = [i] * . + /// It corresponds to the scal method defined by BLAS1. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Multiply(ReadOnlySpan x, T y, Span destination) + where T : IMultiplyOperators, IMultiplicativeIdentity => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of and length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ([i] * [i]) + [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan addend, Span destination) + where T : IAdditionOperators, IMultiplyOperators => + InvokeSpanSpanSpanIntoSpan>(x, y, addend, destination); + + /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The third tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ([i] * [i]) + . + /// It corresponds to the axpy method defined by BLAS1. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, T addend, Span destination) + where T : IAdditionOperators, IMultiplyOperators => + InvokeSpanSpanScalarIntoSpan>(x, y, addend, destination); + + /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ([i] * ) + [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void MultiplyAdd(ReadOnlySpan x, T y, ReadOnlySpan addend, Span destination) + where T : IAdditionOperators, IMultiplyOperators => + InvokeSpanScalarSpanIntoSpan>(x, y, addend, destination); + + /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of and length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ([i] * [i]) + [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void FusedMultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan addend, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanSpanIntoSpan>(x, y, addend, destination); + + /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The third tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ([i] * [i]) + . + /// It corresponds to the axpy method defined by BLAS1. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void FusedMultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, T addend, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanSpanScalarIntoSpan>(x, y, addend, destination); + + /// Computes the element-wise result of ( * ) * for the specified tensors of numbers. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The third tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ([i] * ) + [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void FusedMultiplyAdd(ReadOnlySpan x, T y, ReadOnlySpan addend, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanScalarSpanIntoSpan>(x, y, addend, destination); + + /// Computes the element-wise negation of each number in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = -[i]. + /// + /// + /// If any of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Negate(ReadOnlySpan x, Span destination) + where T : IUnaryNegationOperators => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the Euclidean norm of the specified tensor of numbers. + /// The first tensor, represented as a span. + /// The norm. + /// + /// + /// This method effectively computes .Sqrt(TensorPrimitives.SumOfSquares(x)). + /// This is often referred to as the Euclidean norm or L2 norm. + /// It corresponds to the nrm2 method defined by BLAS1. + /// + /// + /// If any of the input values is equal to , the result value is also NaN. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Norm(ReadOnlySpan x) + where T : IRootFunctions => + T.Sqrt(SumOfSquares(x)); + + /// Computes the element-wise one's complement of numbers in the specified tensor. + /// The first tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = ~[i]. + /// + /// + public static void OnesComplement(ReadOnlySpan x, Span destination) + where T : IBitwiseOperators => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise population count of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.PopCount([i]). + /// + /// + public static void PopCount(ReadOnlySpan x, Span destination) + where T : IBinaryInteger => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Pow([i], [i]). + /// + /// + public static void Pow(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IPowerFunctions => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Pow([i], ). + /// + /// + public static void Pow(ReadOnlySpan x, T y, Span destination) + where T : IPowerFunctions => + InvokeSpanScalarIntoSpan>(x, y, destination); + + /// Computes the element-wise power of a number in a specified tensor raised to a number in another specified tensors. + /// The first tensor, represented as a scalar. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Pow(, [i]). + /// + /// + public static void Pow(T x, ReadOnlySpan y, Span destination) + where T : IPowerFunctions => + InvokeScalarSpanIntoSpan>(x, y, destination); + + /// Computes the product of all elements in the specified non-empty tensor of numbers. + /// The tensor, represented as a span. + /// The result of multiplying all elements in . + /// Length of must be greater than zero. + /// + /// + /// If any of the input values is equal to , the result value is also NaN. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Product(ReadOnlySpan x) + where T : IMultiplyOperators, IMultiplicativeIdentity + { + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + return Aggregate, MultiplyOperator>(x); + } + + /// Computes the product of the element-wise differences of the numbers in the specified non-empty tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The result of multiplying the element-wise subtraction of the elements in the second tensor from the first tensor. + /// Length of both input spans must be greater than zero. + /// and must have the same length. + /// + /// + /// This method effectively computes: + /// + /// Span<T> differences = ...; + /// TensorPrimitives.Subtract(x, y, differences); + /// T result = TensorPrimitives.Product(differences); + /// + /// but without requiring additional temporary storage for the intermediate differences. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T ProductOfDifferences(ReadOnlySpan x, ReadOnlySpan y) + where T : ISubtractionOperators, IMultiplyOperators, IMultiplicativeIdentity + { + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + return Aggregate, MultiplyOperator>(x, y); + } + + /// Computes the product of the element-wise sums of the numbers in the specified non-empty tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The result of multiplying the element-wise additions of the elements in each tensor. + /// Length of both input spans must be greater than zero. + /// and must have the same length. + /// + /// + /// This method effectively computes: + /// + /// Span<T> sums = ...; + /// TensorPrimitives.Add(x, y, sums); + /// T result = TensorPrimitives.Product(sums); + /// + /// but without requiring additional temporary storage for the intermediate sums. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T ProductOfSums(ReadOnlySpan x, ReadOnlySpan y) + where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators, IMultiplicativeIdentity + { + if (x.IsEmpty) + { + ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + } + + return Aggregate, MultiplyOperator>(x, y); + } + + /// Computes the element-wise conversion of each number of radians in the specified tensor to degrees. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .RadiansToDegrees([i]). + /// + /// + public static void RadiansToDegrees(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise reciprocal of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and an element in is equal to zero. + /// + /// + /// This method effectively computes [i] = 1 / [i]. + /// + /// + public static void Reciprocal(ReadOnlySpan x, Span destination) + where T : IFloatingPoint => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise reciprocal of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and an element in is equal to zero. + /// + /// + /// This method effectively computes [i] = 1 / [i]. + /// + /// + public static void ReciprocalEstimate(ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise reciprocal of the square root of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and an element in is equal to zero. + /// + /// + /// This method effectively computes [i] = 1 / [i]. + /// + /// + public static void ReciprocalSqrt(ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise reciprocal of the square root of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// is an integer type and an element in is equal to zero. + /// + /// + /// This method effectively computes [i] = 1 / [i]. + /// + /// + public static void ReciprocalSqrtEstimate(ReadOnlySpan x, Span destination) + where T : IFloatingPointIeee754 => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// The degree of the root to be computed, represented as a scalar. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.RootN([i], ). + /// + /// + public static void RootN(ReadOnlySpan x, int n, Span destination) + where T : IRootFunctions + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.RootN(x[i], n); + } + } + + /// Computes the element-wise rotation left of numbers in the specified tensor by the specified rotation amount. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// The number of bits to rotate, represented as a scalar. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.RotateLeft([i], ). + /// + /// + public static void RotateLeft(ReadOnlySpan x, int rotateAmount, Span destination) + where T : IBinaryInteger + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.RotateLeft(x[i], rotateAmount); + } + } + + /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// The number of bits to rotate, represented as a scalar. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.RotateRight([i], ). + /// + /// + public static void RotateRight(ReadOnlySpan x, int rotateAmount, Span destination) + where T : IBinaryInteger + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.RotateRight(x[i], rotateAmount); + } + } + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Round([i]). + /// + /// + public static void Round(ReadOnlySpan x, Span destination) + where T : IFloatingPoint => + Round(x, digits: 0, MidpointRounding.ToEven, destination); + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The tensor, represented as a span. + /// The mode under which should be rounded. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Round([i], ). + /// + /// + public static void Round(ReadOnlySpan x, MidpointRounding mode, Span destination) + where T : IFloatingPoint => + Round(x, digits: 0, mode, destination); + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The tensor, represented as a span. + /// The number of fractional digits to which the numbers in should be rounded. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Round([i], ). + /// + /// + public static void Round(ReadOnlySpan x, int digits, Span destination) where T : IFloatingPoint => + Round(x, digits, MidpointRounding.ToEven, destination); + + /// Computes the element-wise rounding of the numbers in the specified tensor + /// The tensor, represented as a span. + /// The number of fractional digits to which the numbers in should be rounded. + /// The mode under which should be rounded. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Round([i], , ). + /// + /// + public static void Round(ReadOnlySpan x, int digits, MidpointRounding mode, Span destination) + where T : IFloatingPoint + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.Round(x[i], digits, mode); + } + } + + /// Computes the element-wise product of numbers in the specified tensor and their base-radix raised to the specified power. + /// The tensor, represented as a span. + /// The value to which base-radix is raised before multipliying x, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.ILogB([i]). + /// + /// + public static void ScaleB(ReadOnlySpan x, int n, Span destination) + where T : IFloatingPointIeee754 + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + ValidateInputOutputSpanNonOverlapping(x, destination); - /// Computes the product of all elements in the specified non-empty tensor of numbers. + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = T.ScaleB(x[i], n); + } + } + + /// Computes the element-wise shifting left of numbers in the specified tensor by the specified shift amount. /// The tensor, represented as a span. - /// The result of multiplying all elements in . - /// Length of must be greater than zero. + /// The destination tensor, represented as a span. + /// The number of bits to shift, represented as a scalar. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// If any of the input values is equal to , the result value is also NaN. - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// This method effectively computes [i] = [i] << . /// /// - public static T Product(ReadOnlySpan x) - where T : IMultiplyOperators, IMultiplicativeIdentity + public static void ShiftLeft(ReadOnlySpan x, int shiftAmount, Span destination) + where T : IBinaryInteger { - if (x.IsEmpty) + if (x.Length > destination.Length) { - ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } - return Aggregate, MultiplyOperator>(x); + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = x[i] << shiftAmount; + } } - /// Computes the product of the element-wise differences of the numbers in the specified non-empty tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The result of multiplying the element-wise subtraction of the elements in the second tensor from the first tensor. - /// Length of both input spans must be greater than zero. - /// and must have the same length. + /// Computes the element-wise arithmetic (signed) shifting right of numbers in the specified tensor by the specified shift amount. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// The number of bits to shift, represented as a scalar. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes: - /// - /// Span<T> differences = ...; - /// TensorPrimitives.Subtract(x, y, differences); - /// T result = TensorPrimitives.Product(differences); - /// - /// but without requiring additional temporary storage for the intermediate differences. - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// This method effectively computes [i] = [i] >> . /// /// - public static T ProductOfDifferences(ReadOnlySpan x, ReadOnlySpan y) - where T : ISubtractionOperators, IMultiplyOperators, IMultiplicativeIdentity + public static void ShiftRightArithmetic(ReadOnlySpan x, int shiftAmount, Span destination) + where T : IBinaryInteger { - if (x.IsEmpty) + if (x.Length > destination.Length) { - ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } - return Aggregate, MultiplyOperator>(x, y); + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = x[i] >> shiftAmount; + } } - /// Computes the product of the element-wise sums of the numbers in the specified non-empty tensors. - /// The first tensor, represented as a span. - /// The second tensor, represented as a span. - /// The result of multiplying the element-wise additions of the elements in each tensor. - /// Length of both input spans must be greater than zero. - /// and must have the same length. + /// Computes the element-wise logical (unsigned) shifting right of numbers in the specified tensor by the specified shift amount. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// The number of bits to shift, represented as a scalar. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes: - /// - /// Span<T> sums = ...; - /// TensorPrimitives.Add(x, y, sums); - /// T result = TensorPrimitives.Product(sums); - /// - /// but without requiring additional temporary storage for the intermediate sums. - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. + /// This method effectively computes [i] = [i] >>> . /// /// - public static T ProductOfSums(ReadOnlySpan x, ReadOnlySpan y) - where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators, IMultiplicativeIdentity + public static void ShiftRightLogical(ReadOnlySpan x, int shiftAmount, Span destination) + where T : IBinaryInteger { - if (x.IsEmpty) + if (x.Length > destination.Length) { - ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); + ThrowHelper.ThrowArgument_DestinationTooShort(); } - return Aggregate, MultiplyOperator>(x, y); + ValidateInputOutputSpanNonOverlapping(x, destination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + destination[i] = x[i] >>> shiftAmount; + } } /// Computes the element-wise sigmoid function on the specified non-empty tensor of numbers. @@ -829,7 +2282,7 @@ public static T ProductOfSums(ReadOnlySpan x, ReadOnlySpan y) /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = 1f / (1f + .Exp(-[i])). + /// This method effectively computes [i] = 1f / (1f + .Exp(-[i])). /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different @@ -847,6 +2300,48 @@ public static void Sigmoid(ReadOnlySpan x, Span destination) InvokeSpanIntoSpan>(x, destination); } + /// Computes the element-wise sine of the value in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Sin([i]). + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Sin(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise sine of the value in the specified tensor that has been multiplied by Pi. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .SinPi([i]). + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void SinPi(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); + /// Computes the element-wise hyperbolic sine of each radian angle in the specified tensor. /// The tensor, represented as a span. /// The destination tensor, represented as a span. @@ -854,14 +2349,14 @@ public static void Sigmoid(ReadOnlySpan x, Span destination) /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = .Sinh([i]). + /// This method effectively computes [i] = .Sinh([i]). /// /// /// If a value is equal to , , or , /// the corresponding destination location is set to that value. /// /// - /// The angles in x must be in radians. Use or multiply by /180 to convert degrees to radians. + /// The angles in x must be in radians. Use or multiply by .Pi / 180 to convert degrees to radians. /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different @@ -872,6 +2367,80 @@ public static void Sinh(ReadOnlySpan x, Span destination) where T : IHyperbolicFunctions => InvokeSpanIntoSpan>(x, destination); + /// Computes the element-wise sine and cosine of the value in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor for the element-wise sine result, represented as a span. + /// The destination tensor for the element-wise cosine result, represented as a span. + /// Destination is too short. + /// and or reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes ([i], [i]) = .SinCos([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void SinCos(ReadOnlySpan x, Span sinDestination, Span cosDestination) + where T : ITrigonometricFunctions + { + if (x.Length > sinDestination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(nameof(sinDestination)); + } + if (x.Length > cosDestination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(nameof(cosDestination)); + } + + ValidateInputOutputSpanNonOverlapping(x, sinDestination); + ValidateInputOutputSpanNonOverlapping(x, cosDestination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + (sinDestination[i], cosDestination[i]) = T.SinCos(x[i]); + } + } + + /// Computes the element-wise sine and cosine of the value in the specified tensor that has been multiplied by Pi. + /// The tensor, represented as a span. + /// The destination tensor for the element-wise sine result, represented as a span. + /// The destination tensor for the element-wise cosine result, represented as a span. + /// Destination is too short. + /// and or reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes ([i], [i]) = .SinCos([i]). + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void SinCosPi(ReadOnlySpan x, Span sinPiDestination, Span cosPiDestination) + where T : ITrigonometricFunctions + { + if (x.Length > sinPiDestination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(nameof(sinPiDestination)); + } + if (x.Length > cosPiDestination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(nameof(cosPiDestination)); + } + + ValidateInputOutputSpanNonOverlapping(x, sinPiDestination); + ValidateInputOutputSpanNonOverlapping(x, cosPiDestination); + + // TODO: Vectorize + for (int i = 0; i < x.Length; i++) + { + (sinPiDestination[i], cosPiDestination[i]) = T.SinCosPi(x[i]); + } + } + /// Computes the softmax function over the specified non-empty tensor of numbers. /// The tensor, represented as a span. /// The destination tensor. @@ -880,8 +2449,8 @@ public static void Sinh(ReadOnlySpan x, Span destination) /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes a sum of MathF.Exp(x[i]) for all elements in . - /// It then effectively computes [i] = MathF.Exp([i]) / sum. + /// This method effectively computes a sum of .Exp(x[i]) for all elements in . + /// It then effectively computes [i] = .Exp([i]) / sum. /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different @@ -908,6 +2477,20 @@ public static void SoftMax(ReadOnlySpan x, Span destination) InvokeSpanScalarIntoSpan, DivideOperator>(x, expSum, destination); } + /// Computes the element-wise square root of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Sqrt([i]). + /// + /// + public static void Sqrt(ReadOnlySpan x, Span destination) + where T : IRootFunctions => + InvokeSpanIntoSpan>(x, destination); + /// Computes the element-wise difference between numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a scalar. @@ -946,6 +2529,24 @@ public static void Subtract(ReadOnlySpan x, T y, Span destination) where T : ISubtractionOperators => InvokeSpanScalarIntoSpan>(x, y, destination); + /// Computes the element-wise difference between numbers in the specified tensors. + /// The first tensor, represented as a scalar. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = - [i]. + /// + /// + /// If either of the element-wise input values is equal to , the resulting element-wise value is also NaN. + /// + /// + public static void Subtract(T x, ReadOnlySpan y, Span destination) + where T : ISubtractionOperators => + InvokeScalarSpanIntoSpan>(x, y, destination); + /// Computes the sum of all elements in the specified tensor of numbers. /// The tensor, represented as a span. /// The result of adding all elements in , or zero if is empty. @@ -1007,6 +2608,48 @@ public static T SumOfSquares(ReadOnlySpan x) where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators => Aggregate, AddOperator>(x); + /// Computes the element-wise tangent of the value in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .Tan([i]). + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void Tan(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise tangent of the value in the specified tensor that has been multiplied by Pi. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = .TanPi([i]). + /// + /// + /// The angles in x must be in radians. Use or multiply by .Pi/180 to convert degrees to radians. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static void TanPi(ReadOnlySpan x, Span destination) + where T : ITrigonometricFunctions => + InvokeSpanIntoSpan>(x, destination); + /// Computes the element-wise hyperbolic tangent of each radian angle in the specified tensor. /// The tensor, represented as a span. /// The destination tensor, represented as a span. @@ -1014,7 +2657,7 @@ public static T SumOfSquares(ReadOnlySpan x) /// and reference overlapping memory locations and do not begin at the same location. /// /// - /// This method effectively computes [i] = .Tanh([i]). + /// This method effectively computes [i] = .Tanh([i]). /// /// /// If a value is equal to , the corresponding destination location is set to -1. @@ -1022,7 +2665,7 @@ public static T SumOfSquares(ReadOnlySpan x) /// If a value is equal to , the corresponding destination location is set to NaN. /// /// - /// The angles in x must be in radians. Use or multiply by /180 to convert degrees to radians. + /// The angles in x must be in radians. Use or multiply by .Pi / 180 to convert degrees to radians. /// /// /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different @@ -1032,5 +2675,65 @@ public static T SumOfSquares(ReadOnlySpan x) public static void Tanh(ReadOnlySpan x, Span destination) where T : IHyperbolicFunctions => InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise trailing zero count of numbers in the specified tensor. + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.TrailingZeroCount([i]). + /// + /// + public static void TrailingZeroCount(ReadOnlySpan x, Span destination) + where T : IBinaryInteger => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise truncation of numbers in the specified tensor. + /// The first tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = T.Truncate([i]). + /// + /// + public static void Truncate(ReadOnlySpan x, Span destination) + where T : IFloatingPoint => + InvokeSpanIntoSpan>(x, destination); + + /// Computes the element-wise XOR of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of must be same as length of . + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = [i] ^ [i]. + /// + /// + public static void Xor(ReadOnlySpan x, ReadOnlySpan y, Span destination) + where T : IBitwiseOperators => + InvokeSpanSpanIntoSpan>(x, y, destination); + + /// Computes the element-wise XOR of numbers in the specified tensors. + /// The first tensor, represented as a span. + /// The second tensor, represented as a scalar. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// and reference overlapping memory locations and do not begin at the same location. + /// + /// + /// This method effectively computes [i] = [i] ^ . + /// + /// + public static void Xor(ReadOnlySpan x, T y, Span destination) + where T : IBitwiseOperators => + InvokeSpanScalarIntoSpan>(x, y, destination); } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs index 41560fbb8b284f..1550e3d6bf7d4c 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs @@ -11,9 +11,8 @@ #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type // TODO: -// - Vectorize the trig-related functions for Ts other than floats +// - Vectorize remaining trig-related functions (some aren't vectorized at all, some are only vectorized for float). // - Vectorize integer operations when sizeof(T) == 1 or 2 (currently only vectorized in most operations for sizeof(T) == 4 or 8). -// - Implement generic version of IndexOfMinMaxCore and corresponding IndexOf methods. namespace System.Numerics.Tensors { @@ -1624,7 +1623,7 @@ private static T Aggregate( nuint remainder = (uint)x.Length; - if (Vector512.IsHardwareAccelerated && Vector512.IsSupported && Unsafe.SizeOf() >= 4) + if (Vector512.IsHardwareAccelerated && Vector512.IsSupported && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { T result; @@ -1644,7 +1643,7 @@ private static T Aggregate( return result; } - if (Vector256.IsHardwareAccelerated && Vector256.IsSupported && Unsafe.SizeOf() >= 4) + if (Vector256.IsHardwareAccelerated && Vector256.IsSupported && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { T result; @@ -1664,7 +1663,7 @@ private static T Aggregate( return result; } - if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && Unsafe.SizeOf() >= 4) + if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { T result; @@ -2724,8 +2723,9 @@ private static T MinMaxCore(ReadOnlySpan x) return curResult; } - private static int IndexOfMinMaxCore(ReadOnlySpan x) - where TIndexOfMinMax : struct, IIndexOfOperator + private static int IndexOfMinMaxCore(ReadOnlySpan x) + where T : INumber + where TIndexOfMinMax : struct, IIndexOfOperator { if (x.IsEmpty) { @@ -2737,193 +2737,270 @@ private static int IndexOfMinMaxCore(ReadOnlySpan x) // otherwise returns the index of the greater of the inputs. // It treats +0 as greater than -0 as per the specification. - if (Vector512.IsHardwareAccelerated && x.Length >= Vector512.Count) + if (Vector512.IsHardwareAccelerated && Vector512.IsSupported && x.Length >= Vector512.Count) { - ref float xRef = ref MemoryMarshal.GetReference(x); - Vector512 resultIndex = Vector512.Create(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - Vector512 curIndex = resultIndex; - Vector512 increment = Vector512.Create(Vector512.Count); + Debug.Assert(sizeof(T) is 1 or 2 or 4 or 8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Vector512 CreateVector512T(int i) => + sizeof(T) == sizeof(long) ? Vector512.Create((long)i).As() : + sizeof(T) == sizeof(int) ? Vector512.Create(i).As() : + sizeof(T) == sizeof(short) ? Vector512.Create((short)i).As() : + Vector512.Create((byte)i).As(); + + ref T xRef = ref MemoryMarshal.GetReference(x); + Vector512 resultIndex = + sizeof(T) == sizeof(long) ? Vector512.Create(0L, 1, 2, 3, 4, 5, 6, 7).As() : + sizeof(T) == sizeof(int) ? Vector512.Create(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).As() : + sizeof(T) == sizeof(short) ? Vector512.Create(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31).As() : + Vector512.Create((byte)0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63).As(); + Vector512 currentIndex = resultIndex; + Vector512 increment = CreateVector512T(Vector512.Count); // Load the first vector as the initial set of results, and bail immediately // to scalar handling if it contains any NaNs (which don't compare equally to themselves). - Vector512 result = Vector512.LoadUnsafe(ref xRef); - Vector512 current; + Vector512 result = Vector512.LoadUnsafe(ref xRef); + Vector512 current; - Vector512 nanMask = ~Vector512.Equals(result, result); - if (nanMask != Vector512.Zero) + Vector512 nanMask; + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return IndexOfFirstMatch(nanMask); + nanMask = ~Vector512.Equals(result, result); + if (nanMask != Vector512.Zero) + { + return IndexOfFirstMatch(nanMask); + } } - int oneVectorFromEnd = x.Length - Vector512.Count; - int i = Vector512.Count; + int oneVectorFromEnd = x.Length - Vector512.Count; + int i = Vector512.Count; // Aggregate additional vectors into the result as long as there's at least one full vector left to process. while (i <= oneVectorFromEnd) { // Load the next vector, and early exit on NaN. current = Vector512.LoadUnsafe(ref xRef, (uint)i); - curIndex += increment; + currentIndex += increment; - nanMask = ~Vector512.Equals(current, current); - if (nanMask != Vector512.Zero) + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return i + IndexOfFirstMatch(nanMask); + nanMask = ~Vector512.Equals(current, current); + if (nanMask != Vector512.Zero) + { + return i + IndexOfFirstMatch(nanMask); + } } - TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, curIndex); + TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, currentIndex); - i += Vector512.Count; + i += Vector512.Count; } // If any elements remain, handle them in one final vector. if (i != x.Length) { - current = Vector512.LoadUnsafe(ref xRef, (uint)(x.Length - Vector512.Count)); - curIndex += Vector512.Create(x.Length - i); + current = Vector512.LoadUnsafe(ref xRef, (uint)(x.Length - Vector512.Count)); + currentIndex += CreateVector512T(x.Length - i); - nanMask = ~Vector512.Equals(current, current); - if (nanMask != Vector512.Zero) + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return curIndex[IndexOfFirstMatch(nanMask)]; + nanMask = ~Vector512.Equals(current, current); + if (nanMask != Vector512.Zero) + { + int indexInVectorOfFirstMatch = IndexOfFirstMatch(nanMask); + return typeof(T) == typeof(double) ? + (int)(long)(object)currentIndex.As()[indexInVectorOfFirstMatch] : + (int)(object)currentIndex.As()[indexInVectorOfFirstMatch]; + } } - TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, curIndex); + TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, currentIndex); } // Aggregate the lanes in the vector to create the final scalar result. - return TIndexOfMinMax.Invoke(result, resultIndex); + return IndexOfFinalAggregate(result, resultIndex); } - if (Vector256.IsHardwareAccelerated && x.Length >= Vector256.Count) + if (Vector256.IsHardwareAccelerated && Vector256.IsSupported && x.Length >= Vector256.Count) { - ref float xRef = ref MemoryMarshal.GetReference(x); - Vector256 resultIndex = Vector256.Create(0, 1, 2, 3, 4, 5, 6, 7); - Vector256 curIndex = resultIndex; - Vector256 increment = Vector256.Create(Vector256.Count); + Debug.Assert(sizeof(T) is 1 or 2 or 4 or 8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Vector256 CreateVector256T(int i) => + sizeof(T) == sizeof(long) ? Vector256.Create((long)i).As() : + sizeof(T) == sizeof(int) ? Vector256.Create(i).As() : + sizeof(T) == sizeof(short) ? Vector256.Create((short)i).As() : + Vector256.Create((byte)i).As(); + + ref T xRef = ref MemoryMarshal.GetReference(x); + Vector256 resultIndex = + sizeof(T) == sizeof(long) ? Vector256.Create(0L, 1, 2, 3).As() : + sizeof(T) == sizeof(int) ? Vector256.Create(0, 1, 2, 3, 4, 5, 6, 7).As() : + sizeof(T) == sizeof(short) ? Vector256.Create(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).As() : + Vector256.Create((byte)0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31).As(); + Vector256 currentIndex = resultIndex; + Vector256 increment = CreateVector256T(Vector256.Count); // Load the first vector as the initial set of results, and bail immediately // to scalar handling if it contains any NaNs (which don't compare equally to themselves). - Vector256 result = Vector256.LoadUnsafe(ref xRef); - Vector256 current; + Vector256 result = Vector256.LoadUnsafe(ref xRef); + Vector256 current; - Vector256 nanMask = ~Vector256.Equals(result, result); - if (nanMask != Vector256.Zero) + Vector256 nanMask; + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return IndexOfFirstMatch(nanMask); + nanMask = ~Vector256.Equals(result, result); + if (nanMask != Vector256.Zero) + { + return IndexOfFirstMatch(nanMask); + } } - int oneVectorFromEnd = x.Length - Vector256.Count; - int i = Vector256.Count; + int oneVectorFromEnd = x.Length - Vector256.Count; + int i = Vector256.Count; // Aggregate additional vectors into the result as long as there's at least one full vector left to process. while (i <= oneVectorFromEnd) { // Load the next vector, and early exit on NaN. current = Vector256.LoadUnsafe(ref xRef, (uint)i); - curIndex += increment; + currentIndex += increment; - nanMask = ~Vector256.Equals(current, current); - if (nanMask != Vector256.Zero) + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return i + IndexOfFirstMatch(nanMask); + nanMask = ~Vector256.Equals(current, current); + if (nanMask != Vector256.Zero) + { + return i + IndexOfFirstMatch(nanMask); + } } - TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, curIndex); + TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, currentIndex); - i += Vector256.Count; + i += Vector256.Count; } // If any elements remain, handle them in one final vector. if (i != x.Length) { - current = Vector256.LoadUnsafe(ref xRef, (uint)(x.Length - Vector256.Count)); - curIndex += Vector256.Create(x.Length - i); + current = Vector256.LoadUnsafe(ref xRef, (uint)(x.Length - Vector256.Count)); + currentIndex += CreateVector256T(x.Length - i); - nanMask = ~Vector256.Equals(current, current); - if (nanMask != Vector256.Zero) + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return curIndex[IndexOfFirstMatch(nanMask)]; + nanMask = ~Vector256.Equals(current, current); + if (nanMask != Vector256.Zero) + { + int indexInVectorOfFirstMatch = IndexOfFirstMatch(nanMask); + return typeof(T) == typeof(double) ? + (int)(long)(object)currentIndex.As()[indexInVectorOfFirstMatch] : + (int)(object)currentIndex.As()[indexInVectorOfFirstMatch]; + } } - TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, curIndex); + TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, currentIndex); } // Aggregate the lanes in the vector to create the final scalar result. - return TIndexOfMinMax.Invoke(result, resultIndex); + return IndexOfFinalAggregate(result, resultIndex); } - if (Vector128.IsHardwareAccelerated && x.Length >= Vector128.Count) + if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && x.Length >= Vector128.Count) { - ref float xRef = ref MemoryMarshal.GetReference(x); - Vector128 resultIndex = Vector128.Create(0, 1, 2, 3); - Vector128 curIndex = resultIndex; - Vector128 increment = Vector128.Create(Vector128.Count); + Debug.Assert(sizeof(T) is 1 or 2 or 4 or 8); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Vector128 CreateVector128T(int i) => + sizeof(T) == sizeof(long) ? Vector128.Create((long)i).As() : + sizeof(T) == sizeof(int) ? Vector128.Create(i).As() : + sizeof(T) == sizeof(short) ? Vector128.Create((short)i).As() : + Vector128.Create((byte)i).As(); + + ref T xRef = ref MemoryMarshal.GetReference(x); + Vector128 resultIndex = + sizeof(T) == sizeof(long) ? Vector128.Create(0L, 1).As() : + sizeof(T) == sizeof(int) ? Vector128.Create(0, 1, 2, 3).As() : + sizeof(T) == sizeof(short) ? Vector128.Create(0, 1, 2, 3, 4, 5, 6, 7).As() : + Vector128.Create((byte)0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15).As(); + Vector128 currentIndex = resultIndex; + Vector128 increment = CreateVector128T(Vector128.Count); // Load the first vector as the initial set of results, and bail immediately // to scalar handling if it contains any NaNs (which don't compare equally to themselves). - Vector128 result = Vector128.LoadUnsafe(ref xRef); - Vector128 current; + Vector128 result = Vector128.LoadUnsafe(ref xRef); + Vector128 current; - Vector128 nanMask = ~Vector128.Equals(result, result); - if (nanMask != Vector128.Zero) + Vector128 nanMask; + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return IndexOfFirstMatch(nanMask); + nanMask = ~Vector128.Equals(result, result); + if (nanMask != Vector128.Zero) + { + return IndexOfFirstMatch(nanMask); + } } - int oneVectorFromEnd = x.Length - Vector128.Count; - int i = Vector128.Count; + int oneVectorFromEnd = x.Length - Vector128.Count; + int i = Vector128.Count; // Aggregate additional vectors into the result as long as there's at least one full vector left to process. while (i <= oneVectorFromEnd) { // Load the next vector, and early exit on NaN. current = Vector128.LoadUnsafe(ref xRef, (uint)i); - curIndex += increment; + currentIndex += increment; - nanMask = ~Vector128.Equals(current, current); - if (nanMask != Vector128.Zero) + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return i + IndexOfFirstMatch(nanMask); + nanMask = ~Vector128.Equals(current, current); + if (nanMask != Vector128.Zero) + { + return i + IndexOfFirstMatch(nanMask); + } } - TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, curIndex); + TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, currentIndex); - i += Vector128.Count; + i += Vector128.Count; } // If any elements remain, handle them in one final vector. if (i != x.Length) { - curIndex += Vector128.Create(x.Length - i); - - current = Vector128.LoadUnsafe(ref xRef, (uint)(x.Length - Vector128.Count)); + current = Vector128.LoadUnsafe(ref xRef, (uint)(x.Length - Vector128.Count)); + currentIndex += CreateVector128T(x.Length - i); - nanMask = ~Vector128.Equals(current, current); - if (nanMask != Vector128.Zero) + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - return curIndex[IndexOfFirstMatch(nanMask)]; + nanMask = ~Vector128.Equals(current, current); + if (nanMask != Vector128.Zero) + { + int indexInVectorOfFirstMatch = IndexOfFirstMatch(nanMask); + return typeof(T) == typeof(double) ? + (int)(long)(object)currentIndex.As()[indexInVectorOfFirstMatch] : + (int)(object)currentIndex.As()[indexInVectorOfFirstMatch]; + } } - TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, curIndex); + TIndexOfMinMax.Invoke(ref result, current, ref resultIndex, currentIndex); } // Aggregate the lanes in the vector to create the final scalar result. - return TIndexOfMinMax.Invoke(result, resultIndex); + return IndexOfFinalAggregate(result, resultIndex); } // Scalar path used when either vectorization is not supported or the input is too small to vectorize. - float curResult = x[0]; + T curResult = x[0]; int curIn = 0; - if (float.IsNaN(curResult)) + if (T.IsNaN(curResult)) { return curIn; } for (int i = 1; i < x.Length; i++) { - float current = x[i]; - if (float.IsNaN(current)) + T current = x[i]; + if (T.IsNaN(current)) { return i; } @@ -2934,20 +3011,14 @@ private static int IndexOfMinMaxCore(ReadOnlySpan x) return curIn; } - private static int IndexOfFirstMatch(Vector128 mask) - { - return BitOperations.TrailingZeroCount(mask.ExtractMostSignificantBits()); - } + private static int IndexOfFirstMatch(Vector128 mask) => + BitOperations.TrailingZeroCount(mask.ExtractMostSignificantBits()); - private static int IndexOfFirstMatch(Vector256 mask) - { - return BitOperations.TrailingZeroCount(mask.ExtractMostSignificantBits()); - } + private static int IndexOfFirstMatch(Vector256 mask) => + BitOperations.TrailingZeroCount(mask.ExtractMostSignificantBits()); - private static int IndexOfFirstMatch(Vector512 mask) - { - return BitOperations.TrailingZeroCount(mask.ExtractMostSignificantBits()); - } + private static int IndexOfFirstMatch(Vector512 mask) => + BitOperations.TrailingZeroCount(mask.ExtractMostSignificantBits()); /// Performs an element-wise operation on and writes the results to . /// The element type. @@ -3879,7 +3950,7 @@ private static void InvokeSpanSpanIntoSpan( nuint remainder = (uint)x.Length; - if (Vector512.IsHardwareAccelerated && Vector512.IsSupported && Unsafe.SizeOf() >= 4) + if (Vector512.IsHardwareAccelerated && Vector512.IsSupported && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { if (remainder >= (uint)Vector512.Count) { @@ -3897,7 +3968,7 @@ private static void InvokeSpanSpanIntoSpan( return; } - if (Vector256.IsHardwareAccelerated && Vector256.IsSupported && Unsafe.SizeOf() >= 4) + if (Vector256.IsHardwareAccelerated && Vector256.IsSupported && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { if (remainder >= (uint)Vector256.Count) { @@ -3915,7 +3986,7 @@ private static void InvokeSpanSpanIntoSpan( return; } - if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && Unsafe.SizeOf() >= 4) + if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { if (remainder >= (uint)Vector128.Count) { @@ -4857,6 +4928,19 @@ static void VectorizedSmall8(ref T xRef, ref T yRef, ref T dRef, nuint remainder } } + /// + /// Performs an element-wise operation on and , + /// and writes the results to . + /// + /// The element type. + /// + /// Specifies the operation to perform on each element loaded from with . + /// + private static void InvokeScalarSpanIntoSpan( + T x, ReadOnlySpan y, Span destination) + where TBinaryOperator : struct, IBinaryOperator => + InvokeSpanScalarIntoSpan, InvertedBinaryOperator>(y, x, destination); + /// /// Performs an element-wise operation on and , /// and writes the results to . @@ -4904,7 +4988,7 @@ private static void InvokeSpanScalarIntoSpan.IsSupported && TTransformOperator.Vectorizable && Unsafe.SizeOf() >= 4) + if (Vector512.IsHardwareAccelerated && Vector512.IsSupported && TTransformOperator.Vectorizable && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { if (remainder >= (uint)Vector512.Count) { @@ -4922,7 +5006,7 @@ private static void InvokeSpanScalarIntoSpan.IsSupported && TTransformOperator.Vectorizable && Unsafe.SizeOf() >= 4) + if (Vector256.IsHardwareAccelerated && Vector256.IsSupported && TTransformOperator.Vectorizable && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { if (remainder >= (uint)Vector256.Count) { @@ -4940,7 +5024,7 @@ private static void InvokeSpanScalarIntoSpan.IsSupported && TTransformOperator.Vectorizable && Unsafe.SizeOf() >= 4) + if (Vector128.IsHardwareAccelerated && Vector128.IsSupported && TTransformOperator.Vectorizable && TBinaryOperator.Vectorizable && Unsafe.SizeOf() >= 4) { if (remainder >= (uint)Vector128.Count) { @@ -9417,24 +9501,6 @@ private static Vector512 IsNegative(Vector512 vector) return Vector512.LessThan(vector, Vector512.Zero); } - /// Gets whether the specified is positive. - private static bool IsPositive(float f) => float.IsPositive(f); - - /// Gets whether each specified is positive. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 IsPositive(Vector128 vector) => - Vector128.GreaterThan(vector.AsInt32(), Vector128.AllBitsSet).AsSingle(); - - /// Gets whether each specified is positive. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 IsPositive(Vector256 vector) => - Vector256.GreaterThan(vector.AsInt32(), Vector256.AllBitsSet).AsSingle(); - - /// Gets whether each specified is positive. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector512 IsPositive(Vector512 vector) => - Vector512.GreaterThan(vector.AsInt32(), Vector512.AllBitsSet).AsSingle(); - /// /// Gets a vector mask that will be all-ones-set for the last elements /// and zero for all other elements. @@ -9672,6 +9738,8 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt64Mask_8x9)) /// x + y internal readonly struct AddOperator : IAggregationOperator where T : IAdditionOperators, IAdditiveIdentity { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) => x + y; public static Vector128 Invoke(Vector128 x, Vector128 y) => x + y; public static Vector256 Invoke(Vector256 x, Vector256 y) => x + y; @@ -9684,9 +9752,20 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt64Mask_8x9)) public static T IdentityValue => T.AdditiveIdentity; } + private readonly struct InvertedBinaryOperator : IBinaryOperator + where TOperator : IBinaryOperator + { + public static bool Vectorizable => TOperator.Vectorizable; + public static T Invoke(T x, T y) => TOperator.Invoke(y, x); + public static Vector128 Invoke(Vector128 x, Vector128 y) => TOperator.Invoke(y, x); + public static Vector256 Invoke(Vector256 x, Vector256 y) => TOperator.Invoke(y, x); + public static Vector512 Invoke(Vector512 x, Vector512 y) => TOperator.Invoke(y, x); + } + /// x - y internal readonly struct SubtractOperator : IBinaryOperator where T : ISubtractionOperators { + public static bool Vectorizable => true; public static T Invoke(T x, T y) => x - y; public static Vector128 Invoke(Vector128 x, Vector128 y) => x - y; public static Vector256 Invoke(Vector256 x, Vector256 y) => x - y; @@ -9696,6 +9775,8 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt64Mask_8x9)) /// (x - y) * (x - y) internal readonly struct SubtractSquaredOperator : IBinaryOperator where T : ISubtractionOperators, IMultiplyOperators { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) { T tmp = x - y; @@ -9724,6 +9805,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) /// x * y internal readonly struct MultiplyOperator : IAggregationOperator where T : IMultiplyOperators, IMultiplicativeIdentity { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) => x * y; public static Vector128 Invoke(Vector128 x, Vector128 y) => x * y; public static Vector256 Invoke(Vector256 x, Vector256 y) => x * y; @@ -9739,15 +9822,200 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) /// x / y internal readonly struct DivideOperator : IBinaryOperator where T : IDivisionOperators { + public static bool Vectorizable => true; public static T Invoke(T x, T y) => x / y; public static Vector128 Invoke(Vector128 x, Vector128 y) => x / y; public static Vector256 Invoke(Vector256 x, Vector256 y) => x / y; public static Vector512 Invoke(Vector512 x, Vector512 y) => x / y; } + /// T.Ieee754Remainder(x, y) + internal readonly struct Ieee754RemainderOperator : IBinaryOperator where T : IFloatingPointIeee754 + { + public static bool Vectorizable => false; + public static T Invoke(T x, T y) => T.Ieee754Remainder(x, y); + public static Vector128 Invoke(Vector128 x, Vector128 y) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x, Vector256 y) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x, Vector512 y) => throw new NotSupportedException(); + } + + // Ieee754Remainder + + internal readonly struct ReciprocalOperator : IUnaryOperator where T : IFloatingPoint + { + public static bool Vectorizable => true; + public static T Invoke(T x) => T.One / x; + public static Vector128 Invoke(Vector128 x) => Vector128.One / x; + public static Vector256 Invoke(Vector256 x) => Vector256.One / x; + public static Vector512 Invoke(Vector512 x) => Vector512.One / x; + } + + private readonly struct ReciprocalSqrtOperator : IUnaryOperator where T : IFloatingPointIeee754 + { + public static bool Vectorizable => true; + public static T Invoke(T x) => T.One / T.Sqrt(x); + public static Vector128 Invoke(Vector128 x) => Vector128.One / Vector128.Sqrt(x); + public static Vector256 Invoke(Vector256 x) => Vector256.One / Vector256.Sqrt(x); + public static Vector512 Invoke(Vector512 x) => Vector512.One / Vector512.Sqrt(x); + } + + private readonly struct ReciprocalEstimateOperator : IUnaryOperator where T : IFloatingPointIeee754 + { + public static bool Vectorizable => true; + + public static T Invoke(T x) => T.ReciprocalEstimate(x); + + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + if (Sse.IsSupported) + { + return Sse.Reciprocal(x.AsSingle()).As(); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ReciprocalEstimate(x.AsSingle()).As(); + } + } + + return Vector128.One / x; + } + + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + if (Avx.IsSupported) + { + return Avx.Reciprocal(x.AsSingle()).As(); + } + } + + return Vector256.One / x; + } + + public static Vector512 Invoke(Vector512 x) + { + if (Avx512F.IsSupported) + { + if (typeof(T) == typeof(float)) + { + return Avx512F.Reciprocal14(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Avx512F.Reciprocal14(x.AsDouble()).As(); + } + } + + return Vector512.One / x; + } + } + + private readonly struct ReciprocalSqrtEstimateOperator : IUnaryOperator where T : IFloatingPointIeee754 + { + public static bool Vectorizable => true; + + public static T Invoke(T x) => T.ReciprocalSqrtEstimate(x); + + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + if (Sse.IsSupported) + { + return Sse.ReciprocalSqrt(x.AsSingle()).As(); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.ReciprocalSquareRootEstimate(x.AsSingle()).As(); + } + } + + return Vector128.One / Vector128.Sqrt(x); + } + + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + if (Avx.IsSupported) + { + return Avx.ReciprocalSqrt(x.AsSingle()).As(); + } + } + + return Vector256.One / Vector256.Sqrt(x); + } + + public static Vector512 Invoke(Vector512 x) + { + if (Avx512F.IsSupported) + { + if (typeof(T) == typeof(float)) + { + return Avx512F.ReciprocalSqrt14(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Avx512F.ReciprocalSqrt14(x.AsDouble()).As(); + } + } + + return Vector512.One / Vector512.Sqrt(x); + } + } + + /// x & y + internal readonly struct BitwiseAndOperator : IBinaryOperator where T : IBitwiseOperators + { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) => x & y; + public static Vector128 Invoke(Vector128 x, Vector128 y) => x & y; + public static Vector256 Invoke(Vector256 x, Vector256 y) => x & y; + public static Vector512 Invoke(Vector512 x, Vector512 y) => x & y; + } + + /// x | y + internal readonly struct BitwiseOrOperator : IBinaryOperator where T : IBitwiseOperators + { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) => x | y; + public static Vector128 Invoke(Vector128 x, Vector128 y) => x | y; + public static Vector256 Invoke(Vector256 x, Vector256 y) => x | y; + public static Vector512 Invoke(Vector512 x, Vector512 y) => x | y; + } + + /// x ^ y + internal readonly struct XorOperator : IBinaryOperator where T : IBitwiseOperators + { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) => x ^ y; + public static Vector128 Invoke(Vector128 x, Vector128 y) => x ^ y; + public static Vector256 Invoke(Vector256 x, Vector256 y) => x ^ y; + public static Vector512 Invoke(Vector512 x, Vector512 y) => x ^ y; + } + + /// ~x + internal readonly struct OnesComplementOperator : IUnaryOperator where T : IBitwiseOperators + { + public static bool Vectorizable => true; + public static T Invoke(T x) => ~x; + public static Vector128 Invoke(Vector128 x) => ~x; + public static Vector256 Invoke(Vector256 x) => ~x; + public static Vector512 Invoke(Vector512 x) => ~x; + } + /// T.Max(x, y) (but NaNs may not be propagated) internal readonly struct MaxOperator : IAggregationOperator where T : INumber { + public static bool Vectorizable => true; + public static T Invoke(T x, T y) { if (typeof(T) == typeof(Half) || typeof(T) == typeof(float) || typeof(T) == typeof(double)) @@ -9842,256 +10110,350 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) public static T Invoke(Vector512 x) => HorizontalAggregate>(x); } - private interface IIndexOfOperator + private interface IIndexOfOperator where T : INumber { - static abstract int Invoke(ref float result, float current, int resultIndex, int curIndex); - static abstract int Invoke(Vector128 result, Vector128 resultIndex); - static abstract void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 curIndex); - static abstract int Invoke(Vector256 result, Vector256 resultIndex); - static abstract void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 curIndex); - static abstract int Invoke(Vector512 result, Vector512 resultIndex); - static abstract void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 curIndex); + static abstract int Invoke(ref T result, T current, int resultIndex, int currentIndex); + static abstract void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 currentIndex); + static abstract void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 currentIndex); + static abstract void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 currentIndex); } - /// Returns the index of MathF.Max(x, y) - internal readonly struct IndexOfMaxOperator : IIndexOfOperator + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int IndexOfFinalAggregate(Vector128 result, Vector128 resultIndex) + where T : INumber + where TIndexOfOperator : struct, IIndexOfOperator { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector128 result, Vector128 maxIndex) - { - Vector128 tmpResult; - Vector128 tmpIndex; - - tmpResult = Vector128.Shuffle(result, Vector128.Create(2, 3, 0, 1)); - tmpIndex = Vector128.Shuffle(maxIndex, Vector128.Create(2, 3, 0, 1)); - Invoke(ref result, tmpResult, ref maxIndex, tmpIndex); + Vector128 tmpResult; + Vector128 tmpIndex; - tmpResult = Vector128.Shuffle(result, Vector128.Create(1, 0, 3, 2)); - tmpIndex = Vector128.Shuffle(maxIndex, Vector128.Create(1, 0, 3, 2)); - Invoke(ref result, tmpResult, ref maxIndex, tmpIndex); + if (sizeof(T) == 8) + { + // Compare 0 with 1 + tmpResult = Vector128.Shuffle(result.AsInt64(), Vector128.Create(1, 0)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsInt64(), Vector128.Create(1, 0)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - return maxIndex.ToScalar(); + // Return 0 + return (int)resultIndex.As().ToScalar(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector128 max, Vector128 current, ref Vector128 maxIndex, Vector128 curIndex) + if (sizeof(T) == 4) { - Vector128 greaterThanMask = Vector128.GreaterThan(max, current); - - Vector128 equalMask = Vector128.Equals(max, current); - if (equalMask.AsInt32() != Vector128.Zero) - { - Vector128 negativeMask = IsNegative(current); - Vector128 lessThanMask = Vector128.LessThan(maxIndex, curIndex); + // Compare 0,1 with 2,3 + tmpResult = Vector128.Shuffle(result.AsInt32(), Vector128.Create(2, 3, 0, 1)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsInt32(), Vector128.Create(2, 3, 0, 1)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - greaterThanMask |= (negativeMask & equalMask) | (~IsNegative(max) & equalMask & lessThanMask.AsSingle()); - } - - max = ElementWiseSelect(greaterThanMask, max, current); + // Compare 0 with 1 + tmpResult = Vector128.Shuffle(result.AsInt32(), Vector128.Create(1, 0, 3, 2)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsInt32(), Vector128.Create(1, 0, 3, 2)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - maxIndex = ElementWiseSelect(greaterThanMask.AsInt32(), maxIndex, curIndex); + // Return 0 + return resultIndex.As().ToScalar(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector256 result, Vector256 maxIndex) + if (sizeof(T) == 2) { - // Max the upper/lower halves of the Vector256 - Vector128 resultLower = result.GetLower(); - Vector128 indexLower = maxIndex.GetLower(); + // Compare 0,1,2,3 with 4,5,6,7 + tmpResult = Vector128.Shuffle(result.AsInt16(), Vector128.Create(4, 5, 6, 7, 0, 1, 2, 3)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsInt16(), Vector128.Create(4, 5, 6, 7, 0, 1, 2, 3)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); + + // Compare 0,1 with 2,3 + tmpResult = Vector128.Shuffle(result.AsInt16(), Vector128.Create(2, 3, 0, 1, 4, 5, 6, 7)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsInt16(), Vector128.Create(2, 3, 0, 1, 4, 5, 6, 7)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - Invoke(ref resultLower, result.GetUpper(), ref indexLower, maxIndex.GetUpper()); - return Invoke(resultLower, indexLower); + // Compare 0 with 1 + tmpResult = Vector128.Shuffle(result.AsInt16(), Vector128.Create(1, 0, 2, 3, 4, 5, 6, 7)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsInt16(), Vector128.Create(1, 0, 2, 3, 4, 5, 6, 7)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); + + // Return 0 + return resultIndex.As().ToScalar(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector256 max, Vector256 current, ref Vector256 maxIndex, Vector256 curIndex) + if (sizeof(T) == 1) { - Vector256 greaterThanMask = Vector256.GreaterThan(max, current); + // Compare 0,1,2,3,4,5,6,7 with 8,9,10,11,12,13,14,15 + tmpResult = Vector128.Shuffle(result.AsByte(), Vector128.Create((byte)8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsByte(), Vector128.Create((byte)8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - Vector256 equalMask = Vector256.Equals(max, current); - if (equalMask.AsInt32() != Vector256.Zero) - { - Vector256 negativeMask = IsNegative(current); - Vector256 lessThanMask = Vector256.LessThan(maxIndex, curIndex); + // Compare 0,1,2,3 with 4,5,6,7 + tmpResult = Vector128.Shuffle(result.AsByte(), Vector128.Create((byte)4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsByte(), Vector128.Create((byte)4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - greaterThanMask |= (negativeMask & equalMask) | (~IsNegative(max) & equalMask & lessThanMask.AsSingle()); - } + // Compare 0,1 with 2,3 + tmpResult = Vector128.Shuffle(result.AsByte(), Vector128.Create((byte)2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsByte(), Vector128.Create((byte)2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - max = ElementWiseSelect(greaterThanMask, max, current); + // Compare 0 with 1 + tmpResult = Vector128.Shuffle(result.AsByte(), Vector128.Create((byte)1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As(); + tmpIndex = Vector128.Shuffle(resultIndex.AsByte(), Vector128.Create((byte)1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As(); + TIndexOfOperator.Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - maxIndex = ElementWiseSelect(greaterThanMask.AsInt32(), maxIndex, curIndex); + // Return 0 + return resultIndex.As().ToScalar(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector512 result, Vector512 resultIndex) - { - // Min the upper/lower halves of the Vector512 - Vector256 resultLower = result.GetLower(); - Vector256 indexLower = resultIndex.GetLower(); + throw new NotSupportedException(); + } - Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); - return Invoke(resultLower, indexLower); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int IndexOfFinalAggregate(Vector256 result, Vector256 resultIndex) + where T : INumber + where TIndexOfOperator : struct, IIndexOfOperator + { + // Min the upper/lower halves of the Vector256 + Vector128 resultLower = result.GetLower(); + Vector128 indexLower = resultIndex.GetLower(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector512 max, Vector512 current, ref Vector512 maxIndex, Vector512 curIndex) - { - Vector512 greaterThanMask = Vector512.GreaterThan(max, current); + TIndexOfOperator.Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); + return IndexOfFinalAggregate(resultLower, indexLower); + } - Vector512 equalMask = Vector512.Equals(max, current); - if (equalMask.AsInt32() != Vector512.Zero) - { - Vector512 negativeMask = IsNegative(current); - Vector512 lessThanMask = Vector512.LessThan(maxIndex, curIndex); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int IndexOfFinalAggregate(Vector512 result, Vector512 resultIndex) + where T : INumber + where TIndexOfOperator : struct, IIndexOfOperator + { + Vector256 resultLower = result.GetLower(); + Vector256 indexLower = resultIndex.GetLower(); - greaterThanMask |= (negativeMask & equalMask) | (~IsNegative(max) & equalMask & lessThanMask.AsSingle()); - } + TIndexOfOperator.Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); + return IndexOfFinalAggregate(resultLower, indexLower); + } - max = ElementWiseSelect(greaterThanMask, max, current); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 IndexLessThan(Vector128 indices1, Vector128 indices2) => + sizeof(T) == sizeof(long) ? Vector128.LessThan(indices1.AsInt64(), indices2.AsInt64()).As() : + sizeof(T) == sizeof(int) ? Vector128.LessThan(indices1.AsInt32(), indices2.AsInt32()).As() : + sizeof(T) == sizeof(short) ? Vector128.LessThan(indices1.AsInt16(), indices2.AsInt16()).As() : + Vector128.LessThan(indices1.AsByte(), indices2.AsByte()).As(); - maxIndex = ElementWiseSelect(greaterThanMask.AsInt32(), maxIndex, curIndex); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 IndexLessThan(Vector256 indices1, Vector256 indices2) => + sizeof(T) == sizeof(long) ? Vector256.LessThan(indices1.AsInt64(), indices2.AsInt64()).As() : + sizeof(T) == sizeof(int) ? Vector256.LessThan(indices1.AsInt32(), indices2.AsInt32()).As() : + sizeof(T) == sizeof(short) ? Vector256.LessThan(indices1.AsInt16(), indices2.AsInt16()).As() : + Vector256.LessThan(indices1.AsByte(), indices2.AsByte()).As(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 IndexLessThan(Vector512 indices1, Vector512 indices2) => + sizeof(T) == sizeof(long) ? Vector512.LessThan(indices1.AsInt64(), indices2.AsInt64()).As() : + sizeof(T) == sizeof(int) ? Vector512.LessThan(indices1.AsInt32(), indices2.AsInt32()).As() : + sizeof(T) == sizeof(short) ? Vector512.LessThan(indices1.AsInt16(), indices2.AsInt16()).As() : + Vector512.LessThan(indices1.AsByte(), indices2.AsByte()).As(); + /// Returns the index of MathF.Max(x, y) + internal readonly struct IndexOfMaxOperator : IIndexOfOperator where T : INumber + { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(ref float result, float current, int resultIndex, int curIndex) + public static void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 currentIndex) { - if (result == current) + Vector128 useResult = Vector128.GreaterThan(result, current); + Vector128 equalMask = Vector128.Equals(result, current); + + if (equalMask != Vector128.Zero) { - if (IsNegative(result) && !IsNegative(current)) + Vector128 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { - result = current; - return curIndex; + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector128 currentNegative = IsNegative(current); + Vector128 sameSign = Vector128.Equals(IsNegative(result).AsInt32(), currentNegative.AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, currentNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; } - } - else if (current > result) - { - result = current; - return curIndex; } - return resultIndex; + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } - } - internal readonly struct IndexOfMaxMagnitudeOperator : IIndexOfOperator - { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector128 result, Vector128 maxIndex) + public static void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 currentIndex) { - Vector128 tmpResult = Vector128.Shuffle(result, Vector128.Create(2, 3, 0, 1)); - Vector128 tmpIndex = Vector128.Shuffle(maxIndex, Vector128.Create(2, 3, 0, 1)); + Vector256 useResult = Vector256.GreaterThan(result, current); + Vector256 equalMask = Vector256.Equals(result, current); - Invoke(ref result, tmpResult, ref maxIndex, tmpIndex); - - tmpResult = Vector128.Shuffle(result, Vector128.Create(1, 0, 3, 2)); - tmpIndex = Vector128.Shuffle(maxIndex, Vector128.Create(1, 0, 3, 2)); + if (equalMask != Vector256.Zero) + { + Vector256 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector256 currentNegative = IsNegative(current); + Vector256 sameSign = Vector256.Equals(IsNegative(result).AsInt32(), currentNegative.AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, currentNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } + } - Invoke(ref result, tmpResult, ref maxIndex, tmpIndex); - return maxIndex.ToScalar(); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector128 max, Vector128 current, ref Vector128 maxIndex, Vector128 curIndex) + public static void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 currentIndex) { - Vector128 maxMag = Vector128.Abs(max), currentMag = Vector128.Abs(current); - - Vector128 greaterThanMask = Vector128.GreaterThan(maxMag, currentMag); + Vector512 useResult = Vector512.GreaterThan(result, current); + Vector512 equalMask = Vector512.Equals(result, current); - Vector128 equalMask = Vector128.Equals(max, current); - if (equalMask.AsInt32() != Vector128.Zero) + if (equalMask != Vector512.Zero) { - Vector128 negativeMask = IsNegative(current); - Vector128 lessThanMask = Vector128.LessThan(maxIndex, curIndex); - - greaterThanMask |= (negativeMask & equalMask) | (~IsNegative(max) & equalMask & lessThanMask.AsSingle()); + Vector512 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector512 currentNegative = IsNegative(current); + Vector512 sameSign = Vector512.Equals(IsNegative(result).AsInt32(), currentNegative.AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, currentNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - max = ElementWiseSelect(greaterThanMask, max, current); - - maxIndex = ElementWiseSelect(greaterThanMask.AsInt32(), maxIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector256 result, Vector256 maxIndex) + public static int Invoke(ref T result, T current, int resultIndex, int currentIndex) { - // Max the upper/lower halves of the Vector256 - Vector128 resultLower = result.GetLower(); - Vector128 indexLower = maxIndex.GetLower(); + if (result == current) + { + bool resultNegative = IsNegative(result); + if ((resultNegative == IsNegative(current)) ? (currentIndex < resultIndex) : resultNegative) + { + result = current; + return currentIndex; + } + } + else if (current > result) + { + result = current; + return currentIndex; + } - Invoke(ref resultLower, result.GetUpper(), ref indexLower, maxIndex.GetUpper()); - return Invoke(resultLower, indexLower); + return resultIndex; } + } + internal readonly struct IndexOfMaxMagnitudeOperator : IIndexOfOperator where T : INumber + { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector256 max, Vector256 current, ref Vector256 maxIndex, Vector256 curIndex) + public static void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 currentIndex) { - Vector256 maxMag = Vector256.Abs(max), currentMag = Vector256.Abs(current); - - Vector256 greaterThanMask = Vector256.GreaterThan(maxMag, currentMag); + Vector128 resultMag = Vector128.Abs(result), currentMag = Vector128.Abs(current); + Vector128 useResult = Vector128.GreaterThan(resultMag, currentMag); + Vector128 equalMask = Vector128.Equals(resultMag, currentMag); - Vector256 equalMask = Vector256.Equals(max, current); - if (equalMask.AsInt32() != Vector256.Zero) + if (equalMask != Vector128.Zero) { - Vector256 negativeMask = IsNegative(current); - Vector256 lessThanMask = Vector256.LessThan(maxIndex, curIndex); - - greaterThanMask |= (negativeMask & equalMask) | (~IsNegative(max) & equalMask & lessThanMask.AsSingle()); + Vector128 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector128 currentNegative = IsNegative(current); + Vector128 sameSign = Vector128.Equals(IsNegative(result).AsInt32(), currentNegative.AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, currentNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - max = ElementWiseSelect(greaterThanMask, max, current); - - maxIndex = ElementWiseSelect(greaterThanMask.AsInt32(), maxIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector512 result, Vector512 resultIndex) + public static void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 currentIndex) { - // Min the upper/lower halves of the Vector512 - Vector256 resultLower = result.GetLower(); - Vector256 indexLower = resultIndex.GetLower(); + Vector256 resultMag = Vector256.Abs(result), currentMag = Vector256.Abs(current); + Vector256 useResult = Vector256.GreaterThan(resultMag, currentMag); + Vector256 equalMask = Vector256.Equals(resultMag, currentMag); + + if (equalMask != Vector256.Zero) + { + Vector256 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector256 currentNegative = IsNegative(current); + Vector256 sameSign = Vector256.Equals(IsNegative(result).AsInt32(), currentNegative.AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, currentNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } + } - Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); - return Invoke(resultLower, indexLower); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector512 max, Vector512 current, ref Vector512 maxIndex, Vector512 curIndex) + public static void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 currentIndex) { - Vector512 maxMag = Vector512.Abs(max), currentMag = Vector512.Abs(current); - Vector512 greaterThanMask = Vector512.GreaterThan(maxMag, currentMag); + Vector512 resultMag = Vector512.Abs(result), currentMag = Vector512.Abs(current); + Vector512 useResult = Vector512.GreaterThan(resultMag, currentMag); + Vector512 equalMask = Vector512.Equals(resultMag, currentMag); - Vector512 equalMask = Vector512.Equals(max, current); - if (equalMask.AsInt32() != Vector512.Zero) + if (equalMask != Vector512.Zero) { - Vector512 negativeMask = IsNegative(current); - Vector512 lessThanMask = Vector512.LessThan(maxIndex, curIndex); - - greaterThanMask |= (negativeMask & equalMask) | (~IsNegative(max) & equalMask & lessThanMask.AsSingle()); + Vector512 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector512 currentNegative = IsNegative(current); + Vector512 sameSign = Vector512.Equals(IsNegative(result).AsInt32(), currentNegative.AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, currentNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - max = ElementWiseSelect(greaterThanMask, max, current); - - maxIndex = ElementWiseSelect(greaterThanMask.AsInt32(), maxIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(ref float result, float current, int resultIndex, int curIndex) + public static int Invoke(ref T result, T current, int resultIndex, int currentIndex) { - float curMaxAbs = MathF.Abs(result); - float currentAbs = MathF.Abs(current); + T resultMag = T.Abs(result); + T currentMag = T.Abs(current); - if (curMaxAbs == currentAbs) + if (resultMag == currentMag) { - if (IsNegative(result) && !IsNegative(current)) + bool resultNegative = IsNegative(result); + if ((resultNegative == IsNegative(current)) ? (currentIndex < resultIndex) : resultNegative) { result = current; - return curIndex; + return currentIndex; } } - else if (currentAbs > curMaxAbs) + else if (currentMag > resultMag) { result = current; - return curIndex; + return currentIndex; } return resultIndex; @@ -10099,243 +10461,210 @@ public static int Invoke(ref float result, float current, int resultIndex, int c } /// Returns the index of MathF.Min(x, y) - internal readonly struct IndexOfMinOperator : IIndexOfOperator + internal readonly struct IndexOfMinOperator : IIndexOfOperator where T : INumber { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector128 result, Vector128 resultIndex) - { - Vector128 tmpResult = Vector128.Shuffle(result, Vector128.Create(2, 3, 0, 1)); - Vector128 tmpIndex = Vector128.Shuffle(resultIndex, Vector128.Create(2, 3, 0, 1)); - - Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - - tmpResult = Vector128.Shuffle(result, Vector128.Create(1, 0, 3, 2)); - tmpIndex = Vector128.Shuffle(resultIndex, Vector128.Create(1, 0, 3, 2)); - - Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - return resultIndex.ToScalar(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 curIndex) + public static void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 currentIndex) { - Vector128 lessThanMask = Vector128.LessThan(result, current); + Vector128 useResult = Vector128.LessThan(result, current); + Vector128 equalMask = Vector128.Equals(result, current); - Vector128 equalMask = Vector128.Equals(result, current); - if (equalMask.AsInt32() != Vector128.Zero) + if (equalMask != Vector128.Zero) { - Vector128 negativeMask = IsNegative(current); - Vector128 lessThanIndexMask = Vector128.LessThan(resultIndex, curIndex); - - lessThanMask |= (~negativeMask & equalMask) | (IsNegative(result) & equalMask & lessThanIndexMask.AsSingle()); + Vector128 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector128 resultNegative = IsNegative(result); + Vector128 sameSign = Vector128.Equals(resultNegative.AsInt32(), IsNegative(current).AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, resultNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - result = ElementWiseSelect(lessThanMask, result, current); - - resultIndex = ElementWiseSelect(lessThanMask.AsInt32(), resultIndex, curIndex); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector256 result, Vector256 resultIndex) - { - // Min the upper/lower halves of the Vector256 - Vector128 resultLower = result.GetLower(); - Vector128 indexLower = resultIndex.GetLower(); - - Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); - return Invoke(resultLower, indexLower); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 curIndex) + public static void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 currentIndex) { - Vector256 lessThanMask = Vector256.LessThan(result, current); + Vector256 useResult = Vector256.LessThan(result, current); + Vector256 equalMask = Vector256.Equals(result, current); - Vector256 equalMask = Vector256.Equals(result, current); - if (equalMask.AsInt32() != Vector256.Zero) + if (equalMask != Vector256.Zero) { - Vector256 negativeMask = IsNegative(current); - Vector256 lessThanIndexMask = Vector256.LessThan(resultIndex, curIndex); - - lessThanMask |= (~negativeMask & equalMask) | (IsNegative(result) & equalMask & lessThanIndexMask.AsSingle()); + Vector256 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector256 resultNegative = IsNegative(result); + Vector256 sameSign = Vector256.Equals(resultNegative.AsInt32(), IsNegative(current).AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, resultNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - result = ElementWiseSelect(lessThanMask, result, current); - - resultIndex = ElementWiseSelect(lessThanMask.AsInt32(), resultIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector512 result, Vector512 resultIndex) + public static void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 currentIndex) { - // Min the upper/lower halves of the Vector512 - Vector256 resultLower = result.GetLower(); - Vector256 indexLower = resultIndex.GetLower(); + Vector512 useResult = Vector512.LessThan(result, current); + Vector512 equalMask = Vector512.Equals(result, current); - Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); - return Invoke(resultLower, indexLower); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 curIndex) - { - Vector512 lessThanMask = Vector512.LessThan(result, current); - - Vector512 equalMask = Vector512.Equals(result, current); - if (equalMask.AsInt32() != Vector512.Zero) + if (equalMask != Vector512.Zero) { - Vector512 negativeMask = IsNegative(current); - Vector512 lessThanIndexMask = Vector512.LessThan(resultIndex, curIndex); - - lessThanMask |= (~negativeMask & equalMask) | (IsNegative(result) & equalMask & lessThanIndexMask.AsSingle()); + Vector512 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector512 resultNegative = IsNegative(result); + Vector512 sameSign = Vector512.Equals(resultNegative.AsInt32(), IsNegative(current).AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, resultNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - result = ElementWiseSelect(lessThanMask, result, current); - - resultIndex = ElementWiseSelect(lessThanMask.AsInt32(), resultIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(ref float result, float current, int resultIndex, int curIndex) + public static int Invoke(ref T result, T current, int resultIndex, int currentIndex) { if (result == current) { - if (IsPositive(result) && !IsPositive(current)) + bool currentNegative = IsNegative(current); + if ((IsNegative(result) == currentNegative) ? (currentIndex < resultIndex) : currentNegative) { result = current; - return curIndex; + return currentIndex; } } else if (current < result) { result = current; - return curIndex; + return currentIndex; } return resultIndex; } } - internal readonly struct IndexOfMinMagnitudeOperator : IIndexOfOperator + internal readonly struct IndexOfMinMagnitudeOperator : IIndexOfOperator where T : INumber { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector128 result, Vector128 resultIndex) + public static void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 currentIndex) { - Vector128 tmpResult = Vector128.Shuffle(result, Vector128.Create(2, 3, 0, 1)); - Vector128 tmpIndex = Vector128.Shuffle(resultIndex, Vector128.Create(2, 3, 0, 1)); - - Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); + Vector128 resultMag = Vector128.Abs(result), currentMag = Vector128.Abs(current); + Vector128 useResult = Vector128.LessThan(resultMag, currentMag); + Vector128 equalMask = Vector128.Equals(resultMag, currentMag); - tmpResult = Vector128.Shuffle(result, Vector128.Create(1, 0, 3, 2)); - tmpIndex = Vector128.Shuffle(resultIndex, Vector128.Create(1, 0, 3, 2)); - - Invoke(ref result, tmpResult, ref resultIndex, tmpIndex); - return resultIndex.ToScalar(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector128 result, Vector128 current, ref Vector128 resultIndex, Vector128 curIndex) - { - Vector128 minMag = Vector128.Abs(result), currentMag = Vector128.Abs(current); - - Vector128 lessThanMask = Vector128.LessThan(minMag, currentMag); - - Vector128 equalMask = Vector128.Equals(result, current); - if (equalMask.AsInt32() != Vector128.Zero) + if (equalMask != Vector128.Zero) { - Vector128 negativeMask = IsNegative(current); - Vector128 lessThanIndexMask = Vector128.LessThan(resultIndex, curIndex); - - lessThanMask |= (~negativeMask & equalMask) | (IsNegative(result) & equalMask & lessThanIndexMask.AsSingle()); + Vector128 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector128 resultNegative = IsNegative(result); + Vector128 sameSign = Vector128.Equals(resultNegative.AsInt32(), IsNegative(current).AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, resultNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - result = ElementWiseSelect(lessThanMask, result, current); - - resultIndex = ElementWiseSelect(lessThanMask.AsInt32(), resultIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector256 result, Vector256 resultIndex) + public static void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 currentIndex) { - // Min the upper/lower halves of the Vector256 - Vector128 resultLower = result.GetLower(); - Vector128 indexLower = resultIndex.GetLower(); + Vector256 resultMag = Vector256.Abs(result), currentMag = Vector256.Abs(current); + Vector256 useResult = Vector256.LessThan(resultMag, currentMag); + Vector256 equalMask = Vector256.Equals(resultMag, currentMag); - Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); - return Invoke(resultLower, indexLower); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector256 result, Vector256 current, ref Vector256 resultIndex, Vector256 curIndex) - { - Vector256 minMag = Vector256.Abs(result), currentMag = Vector256.Abs(current); - - Vector256 lessThanMask = Vector256.LessThan(minMag, currentMag); - - Vector256 equalMask = Vector256.Equals(result, current); - if (equalMask.AsInt32() != Vector256.Zero) + if (equalMask != Vector256.Zero) { - Vector256 negativeMask = IsNegative(current); - Vector256 lessThanIndexMask = Vector256.LessThan(resultIndex, curIndex); - - lessThanMask |= (~negativeMask & equalMask) | (IsNegative(result) & equalMask & lessThanIndexMask.AsSingle()); + Vector256 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector256 resultNegative = IsNegative(result); + Vector256 sameSign = Vector256.Equals(resultNegative.AsInt32(), IsNegative(current).AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, resultNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - result = ElementWiseSelect(lessThanMask, result, current); - - resultIndex = ElementWiseSelect(lessThanMask.AsInt32(), resultIndex, curIndex); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(Vector512 result, Vector512 resultIndex) - { - // Min the upper/lower halves of the Vector512 - Vector256 resultLower = result.GetLower(); - Vector256 indexLower = resultIndex.GetLower(); - - Invoke(ref resultLower, result.GetUpper(), ref indexLower, resultIndex.GetUpper()); - return Invoke(resultLower, indexLower); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 curIndex) + public static void Invoke(ref Vector512 result, Vector512 current, ref Vector512 resultIndex, Vector512 currentIndex) { - Vector512 minMag = Vector512.Abs(result), currentMag = Vector512.Abs(current); + Vector512 resultMag = Vector512.Abs(result), currentMag = Vector512.Abs(current); + Vector512 useResult = Vector512.LessThan(resultMag, currentMag); + Vector512 equalMask = Vector512.Equals(resultMag, currentMag); - Vector512 lessThanMask = Vector512.LessThan(minMag, currentMag); - - Vector512 equalMask = Vector512.Equals(result, current); - if (equalMask.AsInt32() != Vector512.Zero) + if (equalMask != Vector512.Zero) { - Vector512 negativeMask = IsNegative(current); - Vector512 lessThanIndexMask = Vector512.LessThan(resultIndex, curIndex); - - lessThanMask |= (~negativeMask & equalMask) | (IsNegative(result) & equalMask & lessThanIndexMask.AsSingle()); + Vector512 lessThanIndexMask = IndexLessThan(resultIndex, currentIndex); + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) + { + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector512 resultNegative = IsNegative(result); + Vector512 sameSign = Vector512.Equals(resultNegative.AsInt32(), IsNegative(current).AsInt32()).As(); + useResult |= equalMask & ElementWiseSelect(sameSign, lessThanIndexMask, resultNegative); + } + else + { + useResult |= equalMask & lessThanIndexMask; + } } - result = ElementWiseSelect(lessThanMask, result, current); - - resultIndex = ElementWiseSelect(lessThanMask.AsInt32(), resultIndex, curIndex); + result = ElementWiseSelect(useResult, result, current); + resultIndex = ElementWiseSelect(useResult, resultIndex, currentIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Invoke(ref float result, float current, int resultIndex, int curIndex) + public static int Invoke(ref T result, T current, int resultIndex, int currentIndex) { - float curMinAbs = MathF.Abs(result); - float currentAbs = MathF.Abs(current); - if (curMinAbs == currentAbs) + T resultMag = T.Abs(result); + T currentMag = T.Abs(current); + + if (resultMag == currentMag) { - if (IsPositive(result) && !IsPositive(current)) + bool currentNegative = IsNegative(current); + if ((IsNegative(result) == currentNegative) ? (currentIndex < resultIndex) : currentNegative) { result = current; - return curIndex; + return currentIndex; } } - else if (currentAbs < curMinAbs) + else if (currentMag < resultMag) { result = current; - return curIndex; + return currentIndex; } return resultIndex; @@ -10346,6 +10675,8 @@ public static int Invoke(ref float result, float current, int resultIndex, int c internal readonly struct MaxPropagateNaNOperator : IBinaryOperator where T : INumber { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) => T.Max(x, y); @@ -10419,6 +10750,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) internal readonly struct MaxMagnitudeOperator : IAggregationOperator where T : INumberBase { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) => T.MaxMagnitude(x, y); @@ -10506,6 +10839,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) internal readonly struct MaxMagnitudePropagateNaNOperator : IBinaryOperator where T : INumberBase { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) => T.MaxMagnitude(x, y); @@ -10574,6 +10909,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) internal readonly struct MinOperator : IAggregationOperator where T : INumber { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) { @@ -10647,6 +10984,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) internal readonly struct MinPropagateNaNOperator : IBinaryOperator where T : INumber { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) => T.Min(x, y); @@ -10720,6 +11059,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) internal readonly struct MinMagnitudeOperator : IAggregationOperator where T : INumberBase { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) => T.MinMagnitude(x, y); @@ -10804,6 +11145,8 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) internal readonly struct MinMagnitudePropagateNaNOperator : IBinaryOperator where T : INumberBase { + public static bool Vectorizable => true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Invoke(T x, T y) => T.MinMagnitude(x, y); @@ -10896,6 +11239,24 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) public static Vector512 Invoke(Vector512 x, Vector512 y, Vector512 z) => (x * y) + z; } + /// (x * y) + z + internal readonly struct FusedMultiplyAddOperator : ITernaryOperator where T : IFloatingPointIeee754 + { + public static T Invoke(T x, T y, T z) => FusedMultiplyAdd(x, y, z); + public static Vector128 Invoke(Vector128 x, Vector128 y, Vector128 z) => FusedMultiplyAdd(x, y, z); + public static Vector256 Invoke(Vector256 x, Vector256 y, Vector256 z) => FusedMultiplyAdd(x, y, z); + public static Vector512 Invoke(Vector512 x, Vector512 y, Vector512 z) => FusedMultiplyAdd(x, y, z); + } + + /// (x * (1 - z)) + (y * z) + internal readonly struct LerpOperator : ITernaryOperator where T : IFloatingPointIeee754 + { + public static T Invoke(T x, T y, T amount) => T.Lerp(x, y, amount); + public static Vector128 Invoke(Vector128 x, Vector128 y, Vector128 amount) => (x * (Vector128.One - amount)) + (y * amount); + public static Vector256 Invoke(Vector256 x, Vector256 y, Vector256 amount) => (x * (Vector256.One - amount)) + (y * amount); + public static Vector512 Invoke(Vector512 x, Vector512 y, Vector512 amount) => (x * (Vector512.One - amount)) + (y * amount); + } + /// x internal readonly struct IdentityOperator : IUnaryOperator { @@ -11582,6 +11943,253 @@ public static Vector512 Invoke(Vector512 x) } #endif + /// T.ExpM1(x) + internal readonly struct ExpM1Operator : IUnaryOperator + where T : IExponentialFunctions + { + public static bool Vectorizable => ExpOperator.Vectorizable; + + public static T Invoke(T x) => T.ExpM1(x); + public static Vector128 Invoke(Vector128 x) => ExpOperator.Invoke(x) - Vector128.One; + public static Vector256 Invoke(Vector256 x) => ExpOperator.Invoke(x) - Vector256.One; + public static Vector512 Invoke(Vector512 x) => ExpOperator.Invoke(x) - Vector512.One; + } + + /// T.Exp2(x) + internal readonly struct Exp2Operator : IUnaryOperator + where T : IExponentialFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + + public static T Invoke(T x) => T.Exp2(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.Exp2M1(x) + internal readonly struct Exp2M1Operator : IUnaryOperator + where T : IExponentialFunctions + { + public static bool Vectorizable => Exp2Operator.Vectorizable; + + public static T Invoke(T x) => T.Exp2M1(x); + public static Vector128 Invoke(Vector128 x) => Exp2Operator.Invoke(x) - Vector128.One; + public static Vector256 Invoke(Vector256 x) => Exp2Operator.Invoke(x) - Vector256.One; + public static Vector512 Invoke(Vector512 x) => Exp2Operator.Invoke(x) - Vector512.One; + } + + /// T.Exp10(x) + internal readonly struct Exp10Operator : IUnaryOperator + where T : IExponentialFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + + public static T Invoke(T x) => T.Exp10(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.Exp10M1(x) + internal readonly struct Exp10M1Operator : IUnaryOperator + where T : IExponentialFunctions + { + public static bool Vectorizable => Exp2Operator.Vectorizable; + + public static T Invoke(T x) => T.Exp10M1(x); + public static Vector128 Invoke(Vector128 x) => Exp10Operator.Invoke(x) - Vector128.One; + public static Vector256 Invoke(Vector256 x) => Exp10Operator.Invoke(x) - Vector256.One; + public static Vector512 Invoke(Vector512 x) => Exp10Operator.Invoke(x) - Vector512.One; + } + + /// T.Pow(x, y) + internal readonly struct PowOperator : IBinaryOperator + where T : IPowerFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x, T y) => T.Pow(x, y); + public static Vector128 Invoke(Vector128 x, Vector128 y) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x, Vector256 y) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x, Vector512 y) => throw new NotSupportedException(); + } + + /// T.Sqrt(x) + internal readonly struct SqrtOperator : IUnaryOperator + where T : IRootFunctions + { + public static bool Vectorizable => true; + public static T Invoke(T x) => T.Sqrt(x); + public static Vector128 Invoke(Vector128 x) => Vector128.Sqrt(x); + public static Vector256 Invoke(Vector256 x) => Vector256.Sqrt(x); + public static Vector512 Invoke(Vector512 x) => Vector512.Sqrt(x); + } + + /// T.Cbrt(x) + internal readonly struct CbrtOperator : IUnaryOperator + where T : IRootFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Cbrt(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.Hypot(x, y) + internal readonly struct HypotOperator : IBinaryOperator + where T : IRootFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x, T y) => T.Hypot(x, y); + public static Vector128 Invoke(Vector128 x, Vector128 y) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x, Vector256 y) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x, Vector512 y) => throw new NotSupportedException(); + } + + /// T.Acos(x) + internal readonly struct AcosOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Acos(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.Acosh(x) + internal readonly struct AcoshOperator : IUnaryOperator + where T : IHyperbolicFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Acosh(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.AcosPi(x) + internal readonly struct AcosPiOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => AcosOperator.Vectorizable; + public static T Invoke(T x) => T.AcosPi(x); + public static Vector128 Invoke(Vector128 x) => AcosOperator.Invoke(x) / Vector128.Create(T.Pi); + public static Vector256 Invoke(Vector256 x) => AcosOperator.Invoke(x) / Vector256.Create(T.Pi); + public static Vector512 Invoke(Vector512 x) => AcosOperator.Invoke(x) / Vector512.Create(T.Pi); + } + + /// T.Asin(x) + internal readonly struct AsinOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Asin(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.Asinh(x) + internal readonly struct AsinhOperator : IUnaryOperator + where T : IHyperbolicFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Asinh(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.AsinPi(x) + internal readonly struct AsinPiOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => AsinOperator.Vectorizable; + public static T Invoke(T x) => T.AsinPi(x); + public static Vector128 Invoke(Vector128 x) => AsinOperator.Invoke(x) / Vector128.Create(T.Pi); + public static Vector256 Invoke(Vector256 x) => AsinOperator.Invoke(x) / Vector256.Create(T.Pi); + public static Vector512 Invoke(Vector512 x) => AsinOperator.Invoke(x) / Vector512.Create(T.Pi); + } + + /// T.Atan(x) + internal readonly struct AtanOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Atan(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.Atanh(x) + internal readonly struct AtanhOperator : IUnaryOperator + where T : IHyperbolicFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Atanh(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.AtanPi(x) + internal readonly struct AtanPiOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => AtanOperator.Vectorizable; + public static T Invoke(T x) => T.AtanPi(x); + public static Vector128 Invoke(Vector128 x) => AtanOperator.Invoke(x) / Vector128.Create(T.Pi); + public static Vector256 Invoke(Vector256 x) => AtanOperator.Invoke(x) / Vector256.Create(T.Pi); + public static Vector512 Invoke(Vector512 x) => AtanOperator.Invoke(x) / Vector512.Create(T.Pi); + } + + /// T.Atan2(y, x) + internal readonly struct Atan2Operator : IBinaryOperator + where T : IFloatingPointIeee754 + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T y, T x) => T.Atan2(y, x); + public static Vector128 Invoke(Vector128 y, Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 y, Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 y, Vector512 x) => throw new NotSupportedException(); + } + + /// T.Atan2Pi(y, x) + internal readonly struct Atan2PiOperator : IBinaryOperator + where T : IFloatingPointIeee754 + { + public static bool Vectorizable => Atan2Operator.Vectorizable; + public static T Invoke(T y, T x) => T.Atan2Pi(y, x); + public static Vector128 Invoke(Vector128 y, Vector128 x) => Atan2Operator.Invoke(y, x) / Vector128.Create(T.Pi); + public static Vector256 Invoke(Vector256 y, Vector256 x) => Atan2Operator.Invoke(y, x) / Vector256.Create(T.Pi); + public static Vector512 Invoke(Vector512 y, Vector512 x) => Atan2Operator.Invoke(y, x) / Vector512.Create(T.Pi); + } + + /// T.Cos(x) + internal readonly struct CosOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Cos(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.CosPi(x) + internal readonly struct CosPiOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => CosOperator.Vectorizable; + public static T Invoke(T x) => T.CosPi(x); + public static Vector128 Invoke(Vector128 x) => CosOperator.Invoke(x * Vector128.Create(T.Pi)); + public static Vector256 Invoke(Vector256 x) => CosOperator.Invoke(x * Vector256.Create(T.Pi)); + public static Vector512 Invoke(Vector512 x) => CosOperator.Invoke(x * Vector512.Create(T.Pi)); + } + /// T.Cosh(x) internal readonly struct CoshOperator : IUnaryOperator where T : IHyperbolicFunctions @@ -11646,6 +12254,28 @@ public static Vector512 Invoke(Vector512 t) } } + /// T.Sin(x) + internal readonly struct SinOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Sin(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.SinPi(x) + internal readonly struct SinPiOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => SinOperator.Vectorizable; + public static T Invoke(T x) => T.SinPi(x); + public static Vector128 Invoke(Vector128 x) => SinOperator.Invoke(x * Vector128.Create(T.Pi)); + public static Vector256 Invoke(Vector256 x) => SinOperator.Invoke(x * Vector256.Create(T.Pi)); + public static Vector512 Invoke(Vector512 x) => SinOperator.Invoke(x * Vector512.Create(T.Pi)); + } + /// T.Sinh(x) internal readonly struct SinhOperator : IUnaryOperator where T : IHyperbolicFunctions @@ -11699,6 +12329,28 @@ public static Vector512 Invoke(Vector512 t) } } + /// T.Tan(x) + internal readonly struct TanOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Tan(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.TanPi(x) + internal readonly struct TanPiOperator : IUnaryOperator + where T : ITrigonometricFunctions + { + public static bool Vectorizable => TanOperator.Vectorizable; + public static T Invoke(T x) => T.TanPi(x); + public static Vector128 Invoke(Vector128 x) => TanOperator.Invoke(x * Vector128.Create(T.Pi)); + public static Vector256 Invoke(Vector256 x) => TanOperator.Invoke(x * Vector256.Create(T.Pi)); + public static Vector512 Invoke(Vector512 x) => TanOperator.Invoke(x * Vector512.Create(T.Pi)); + } + /// T.Tanh(x) internal readonly struct TanhOperator : IUnaryOperator where T : IHyperbolicFunctions @@ -13113,6 +13765,61 @@ public static Vector512 Invoke(Vector512 x) } #endif + /// T.Log10(x) + internal readonly struct Log10Operator : IUnaryOperator + where T : ILogarithmicFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.Log10(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.LogP1(x) + internal readonly struct LogP1Operator : IUnaryOperator + where T : ILogarithmicFunctions + { + public static bool Vectorizable => LogOperator.Vectorizable; + public static T Invoke(T x) => T.LogP1(x); + public static Vector128 Invoke(Vector128 x) => LogOperator.Invoke(x + Vector128.One); + public static Vector256 Invoke(Vector256 x) => LogOperator.Invoke(x + Vector256.One); + public static Vector512 Invoke(Vector512 x) => LogOperator.Invoke(x + Vector512.One); + } + + /// T.Log2P1(x) + internal readonly struct Log2P1Operator : IUnaryOperator + where T : ILogarithmicFunctions + { + public static bool Vectorizable => Log2Operator.Vectorizable; + public static T Invoke(T x) => T.Log2P1(x); + public static Vector128 Invoke(Vector128 x) => Log2Operator.Invoke(x + Vector128.One); + public static Vector256 Invoke(Vector256 x) => Log2Operator.Invoke(x + Vector256.One); + public static Vector512 Invoke(Vector512 x) => Log2Operator.Invoke(x + Vector512.One); + } + + /// T.Log10P1(x) + internal readonly struct Log10P1Operator : IUnaryOperator + where T : ILogarithmicFunctions + { + public static bool Vectorizable => Log10Operator.Vectorizable; + public static T Invoke(T x) => T.Log10P1(x); + public static Vector128 Invoke(Vector128 x) => Log10Operator.Invoke(x + Vector128.One); + public static Vector256 Invoke(Vector256 x) => Log10Operator.Invoke(x + Vector256.One); + public static Vector512 Invoke(Vector512 x) => Log10Operator.Invoke(x + Vector512.One); + } + + /// T.Log(x, y) + internal readonly struct LogBaseOperator : IBinaryOperator + where T : ILogarithmicFunctions + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x, T y) => T.Log(x, y); + public static Vector128 Invoke(Vector128 x, Vector128 y) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x, Vector256 y) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x, Vector512 y) => throw new NotSupportedException(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 ElementWiseSelect(Vector128 mask, Vector128 left, Vector128 right) { @@ -13169,7 +13876,7 @@ private static Vector512 ElementWiseSelect(Vector512 mask, Vector512 return Vector512.ConditionalSelect(mask, left, right); } - /// 1f / (1f + MathF.Exp(-x)) + /// 1 / (1 + T.Exp(-x)) internal readonly struct SigmoidOperator : IUnaryOperator where T : IExponentialFunctions { public static bool Vectorizable => typeof(T) == typeof(float); @@ -13179,6 +13886,350 @@ private static Vector512 ElementWiseSelect(Vector512 mask, Vector512 public static Vector512 Invoke(Vector512 x) => Vector512.Create(T.One) / (Vector512.Create(T.One) + ExpOperator.Invoke(-x)); } + internal readonly struct CeilingOperator : IUnaryOperator where T : IFloatingPoint + { + public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double); + + public static T Invoke(T x) => T.Ceiling(x); + + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + return Vector128.Ceiling(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector128.Ceiling(x.AsDouble()).As(); + } + + throw new NotSupportedException(); + } + + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + return Vector256.Ceiling(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector256.Ceiling(x.AsDouble()).As(); + } + + throw new NotSupportedException(); + } + + public static Vector512 Invoke(Vector512 x) + { + if (typeof(T) == typeof(float)) + { + return Vector512.Ceiling(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector512.Ceiling(x.AsDouble()).As(); + } + + throw new NotSupportedException(); + } + } + + internal readonly struct FloorOperator : IUnaryOperator where T : IFloatingPoint + { + public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double); + + public static T Invoke(T x) => T.Floor(x); + + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + return Vector128.Floor(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector128.Floor(x.AsDouble()).As(); + } + + throw new NotSupportedException(); + } + + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + return Vector256.Floor(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector256.Floor(x.AsDouble()).As(); + } + + throw new NotSupportedException(); + } + + public static Vector512 Invoke(Vector512 x) + { + if (typeof(T) == typeof(float)) + { + return Vector512.Floor(x.AsSingle()).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector512.Floor(x.AsDouble()).As(); + } + + throw new NotSupportedException(); + } + } + + private readonly struct TruncateOperator : IUnaryOperator where T : IFloatingPoint + { + public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double); + + public static T Invoke(T x) => T.Truncate(x); + + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + if (Sse41.IsSupported) + { + return Sse41.RoundToZero(x.AsSingle()).As(); + } + + if (AdvSimd.IsSupported) + { + return AdvSimd.RoundToZero(x.AsSingle()).As(); + } + + return Vector128.ConditionalSelect(Vector128.GreaterThanOrEqual(x, Vector128.Zero), + Vector128.Floor(x.AsSingle()).As(), + Vector128.Ceiling(x.AsSingle()).As()); + } + + if (typeof(T) == typeof(double)) + { + if (Sse41.IsSupported) + { + return Sse41.RoundToZero(x.AsDouble()).As(); + } + + return Vector128.ConditionalSelect(Vector128.GreaterThanOrEqual(x, Vector128.Zero), + Vector128.Floor(x.AsDouble()).As(), + Vector128.Ceiling(x.AsDouble()).As()); + } + + throw new NotSupportedException(); + } + + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + if (Avx.IsSupported) + { + return Avx.RoundToZero(x.AsSingle()).As(); + } + + return Vector256.ConditionalSelect(Vector256.GreaterThanOrEqual(x, Vector256.Zero), + Vector256.Floor(x.AsSingle()).As(), + Vector256.Ceiling(x.AsSingle()).As()); + } + + if (typeof(T) == typeof(double)) + { + if (Avx.IsSupported) + { + return Avx.RoundToZero(x.AsDouble()).As(); + } + + return Vector256.ConditionalSelect(Vector256.GreaterThanOrEqual(x, Vector256.Zero), + Vector256.Floor(x.AsDouble()).As(), + Vector256.Ceiling(x.AsDouble()).As()); + } + + throw new NotSupportedException(); + } + + public static Vector512 Invoke(Vector512 x) + { + if (typeof(T) == typeof(float)) + { + if (Avx512F.IsSupported) + { + return Avx512F.RoundScale(x.AsSingle(), 0b11).As(); + } + + return Vector512.ConditionalSelect(Vector512.GreaterThanOrEqual(x, Vector512.Zero), + Vector512.Floor(x.AsSingle()).As(), + Vector512.Ceiling(x.AsSingle()).As()); + } + + if (typeof(T) == typeof(double)) + { + if (Avx512F.IsSupported) + { + return Avx512F.RoundScale(x.AsDouble(), 0b11).As(); + } + + return Vector512.ConditionalSelect(Vector512.GreaterThanOrEqual(x, Vector512.Zero), + Vector512.Floor(x.AsDouble()).As(), + Vector512.Ceiling(x.AsDouble()).As()); + } + + throw new NotSupportedException(); + } + } + + /// T.PopCount(x) + internal readonly struct PopCountOperator : IUnaryOperator where T : IBinaryInteger + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.PopCount(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.LeadingZeroCount(x) + internal readonly struct LeadingZeroCountOperator : IUnaryOperator where T : IBinaryInteger + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.LeadingZeroCount(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + /// T.TrailingZeroCount(x) + internal readonly struct TrailingZeroCountOperator : IUnaryOperator where T : IBinaryInteger + { + public static bool Vectorizable => false; // TODO: Vectorize + public static T Invoke(T x) => T.TrailingZeroCount(x); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + } + + private readonly struct CopySignOperator : IBinaryOperator where T : INumber + { + public static bool Vectorizable => true; + + public static T Invoke(T x, T y) => T.CopySign(x, y); + + public static Vector128 Invoke(Vector128 x, Vector128 y) + { + if (typeof(T) == typeof(float)) + { + return Vector128.ConditionalSelect(Vector128.Create(-0.0f).As(), y, x); + } + + if (typeof(T) == typeof(double)) + { + return Vector128.ConditionalSelect(Vector128.Create(-0.0d).As(), y, x); + } + + if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) + { + Vector128 absValue = Vector128.Abs(x); + Vector128 sign = Vector128.GreaterThanOrEqual(y, Vector128.Zero); + Vector128 error = sign & Vector128.LessThan(absValue, Vector128.Zero); + if (error != Vector128.Zero) + { + Math.Abs(int.MinValue); // throw OverflowException + } + + return Vector128.ConditionalSelect(sign, absValue, -absValue); + } + + return x; + } + + public static Vector256 Invoke(Vector256 x, Vector256 y) + { + if (typeof(T) == typeof(float)) + { + return Vector256.ConditionalSelect(Vector256.Create(-0.0f).As(), y, x); + } + + if (typeof(T) == typeof(double)) + { + return Vector256.ConditionalSelect(Vector256.Create(-0.0d).As(), y, x); + } + + if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) + { + Vector256 absValue = Vector256.Abs(x); + Vector256 sign = Vector256.GreaterThanOrEqual(y, Vector256.Zero); + Vector256 error = sign & Vector256.LessThan(absValue, Vector256.Zero); + if (error != Vector256.Zero) + { + Math.Abs(int.MinValue); // throw OverflowException + } + + return Vector256.ConditionalSelect(sign, absValue, -absValue); + } + + return x; + } + + public static Vector512 Invoke(Vector512 x, Vector512 y) + { + if (typeof(T) == typeof(float)) + { + return Vector512.ConditionalSelect(Vector512.Create(-0.0f).As(), y, x); + } + + if (typeof(T) == typeof(double)) + { + return Vector512.ConditionalSelect(Vector512.Create(-0.0d).As(), y, x); + } + + if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) + { + Vector512 absValue = Vector512.Abs(x); + Vector512 sign = Vector512.GreaterThanOrEqual(y, Vector512.Zero); + Vector512 error = sign & Vector512.LessThan(absValue, Vector512.Zero); + if (error != Vector512.Zero) + { + Math.Abs(int.MinValue); // throw OverflowException + } + + return Vector512.ConditionalSelect(sign, absValue, -absValue); + } + + return x; + } + } + + /// T.DegreesToRadians(x) + internal readonly struct DegreesToRadiansOperator : IUnaryOperator where T : ITrigonometricFunctions + { + public static bool Vectorizable => true; + public static T Invoke(T x) => T.DegreesToRadians(x); + public static Vector128 Invoke(Vector128 x) => (x * T.Pi) / T.CreateChecked(180); + public static Vector256 Invoke(Vector256 x) => (x * T.Pi) / T.CreateChecked(180); + public static Vector512 Invoke(Vector512 x) => (x * T.Pi) / T.CreateChecked(180); + } + + /// T.RadiansToDegrees(x) + internal readonly struct RadiansToDegreesOperator : IUnaryOperator where T : ITrigonometricFunctions + { + public static bool Vectorizable => true; + public static T Invoke(T x) => T.RadiansToDegrees(x); + public static Vector128 Invoke(Vector128 x) => (x * T.CreateChecked(180)) / T.Pi; + public static Vector256 Invoke(Vector256 x) => (x * T.CreateChecked(180)) / T.Pi; + public static Vector512 Invoke(Vector512 x) => (x * T.CreateChecked(180)) / T.Pi; + } + /// Operator that takes one input value and returns a single value. private interface IUnaryOperator { @@ -13192,6 +14243,7 @@ private interface IUnaryOperator /// Operator that takes two input values and returns a single value. private interface IBinaryOperator { + static abstract bool Vectorizable { get; } static abstract T Invoke(T x, T y); static abstract Vector128 Invoke(Vector128 x, Vector128 y); static abstract Vector256 Invoke(Vector256 x, Vector256 y); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netstandard/TensorPrimitives.Single.netstandard.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netstandard/TensorPrimitives.Single.netstandard.cs index fe32027099c7e3..c9474cb470fd7f 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netstandard/TensorPrimitives.Single.netstandard.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netstandard/TensorPrimitives.Single.netstandard.cs @@ -808,9 +808,11 @@ private static float MinMaxCore(ReadOnlySpan x, TMinMaxO private static readonly int[] s_0through7 = [0, 1, 2, 3, 4, 5, 6, 7]; - private static int IndexOfMinMaxCore(ReadOnlySpan x, TIndexOfMinMaxOperator op = default) + private static int IndexOfMinMaxCore(ReadOnlySpan x, TIndexOfMinMaxOperator op = default) where TIndexOfMinMaxOperator : struct, IIndexOfOperator { + Debug.Assert(typeof(T) == typeof(float), "The generic parameter exists only to provide the same signature as the generic implementation."); + if (x.IsEmpty) { return -1; @@ -829,7 +831,7 @@ private static int IndexOfMinMaxCore(ReadOnlySpan ref float xRef = ref MemoryMarshal.GetReference(x); Vector resultIndex = new Vector(s_0through7); - Vector curIndex = resultIndex; + Vector currentIndex = resultIndex; Vector increment = new Vector(Vector.Count); // Load the first vector as the initial set of results, and bail immediately @@ -845,21 +847,21 @@ private static int IndexOfMinMaxCore(ReadOnlySpan { // Load the next vector, and early exit on NaN. current = AsVector(ref xRef, i); - curIndex = Vector.Add(curIndex, increment); + currentIndex = Vector.Add(currentIndex, increment); if (!Vector.EqualsAll(current, current)) { goto Scalar; } - op.Invoke(ref resultVector, current, ref resultIndex, curIndex); + op.Invoke(ref resultVector, current, ref resultIndex, currentIndex); i += Vector.Count; } // If any elements remain, handle them in one final vector. if (i != x.Length) { - curIndex = Vector.Add(curIndex, new Vector(x.Length - i)); + currentIndex = Vector.Add(currentIndex, new Vector(x.Length - i)); current = AsVector(ref xRef, x.Length - Vector.Count); if (!Vector.EqualsAll(current, current)) @@ -867,7 +869,7 @@ private static int IndexOfMinMaxCore(ReadOnlySpan goto Scalar; } - op.Invoke(ref resultVector, current, ref resultIndex, curIndex); + op.Invoke(ref resultVector, current, ref resultIndex, currentIndex); } result = op.Invoke(resultVector, resultIndex); @@ -2966,278 +2968,315 @@ public Vector Invoke(Vector x, Vector y) private interface IIndexOfOperator { - int Invoke(ref float result, float current, int resultIndex, int curIndex); + int Invoke(ref float result, float current, int resultIndex, int currentIndex); int Invoke(Vector result, Vector resultIndex); - void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector curIndex); + void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector currentIndex); } /// Returns the index of MathF.Max(x, y) private readonly struct IndexOfMaxOperator_Single : IIndexOfOperator { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(Vector result, Vector resultIndex) + public int Invoke(ref float result, float current, int resultIndex, int currentIndex) { - float curMax = result[0]; - int curIn = resultIndex[0]; - for (int i = 1; i < Vector.Count; i++) + if (result == current) { - if (result[i] == curMax && IsNegative(curMax) && !IsNegative(result[i])) - { - curMax = result[i]; - curIn = resultIndex[i]; - } - else if (result[i] > curMax) + bool resultNegative = IsNegative(result); + if ((resultNegative == IsNegative(current)) ? (currentIndex < resultIndex) : resultNegative) { - curMax = result[i]; - curIn = resultIndex[i]; + result = current; + return currentIndex; } } + else if (current > result) + { + result = current; + return currentIndex; + } - return curIn; + return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector curIndex) + public int Invoke(Vector current, Vector currentIndex) { - Vector lessThanMask = Vector.GreaterThan(result, current); - - Vector equalMask = Vector.Equals(result, current); + float result = current[0]; + int resultIndex = currentIndex[0]; - if (equalMask != Vector.Zero) + for (int i = 1; i < Vector.Count; i++) { - Vector negativeMask = IsNegative(current); - Vector lessThanIndexMask = Vector.LessThan(resultIndex, curIndex); - - lessThanMask |= ((Vector)~negativeMask & equalMask) | ((Vector)IsNegative(result) & equalMask & lessThanIndexMask); + if (current[i] == result) + { + bool resultNegative = IsNegative(result); + if ((resultNegative == IsNegative(current[i])) ? (currentIndex[i] < resultIndex) : resultNegative) + { + result = current[i]; + resultIndex = currentIndex[i]; + } + } + else if (current[i] > result) + { + result = current[i]; + resultIndex = currentIndex[i]; + } } - result = Vector.ConditionalSelect(lessThanMask, result, current); - - resultIndex = Vector.ConditionalSelect(lessThanMask, resultIndex, curIndex); + return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(ref float result, float current, int resultIndex, int curIndex) + public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector currentIndex) { - if (result == current) - { - if (IsNegative(result) && !IsNegative(current)) - { - result = current; - return curIndex; - } - } - else if (current > result) + Vector useResult = Vector.GreaterThan(result, current); + Vector equalMask = Vector.Equals(result, current); + + if (equalMask != Vector.Zero) { - result = current; - return curIndex; + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector currentNegative = IsNegative(current); + useResult |= + equalMask & + Vector.ConditionalSelect(Vector.Equals(IsNegative(result), currentNegative), + Vector.LessThan(resultIndex, currentIndex), + (Vector)currentNegative); } - return resultIndex; + result = Vector.ConditionalSelect(useResult, result, current); + resultIndex = Vector.ConditionalSelect(useResult, resultIndex, currentIndex); } } private readonly struct IndexOfMaxMagnitudeOperator_Single : IIndexOfOperator { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(ref float result, float current, int resultIndex, int curIndex) + public int Invoke(ref float result, float current, int resultIndex, int currentIndex) { - float curMaxAbs = MathF.Abs(result); + float resultAbs = MathF.Abs(result); float currentAbs = MathF.Abs(current); - if (curMaxAbs == currentAbs) + if (resultAbs == currentAbs) { - if (IsNegative(result) && !IsNegative(current)) + bool resultNegative = IsNegative(result); + if ((resultNegative == IsNegative(current)) ? (currentIndex < resultIndex) : resultNegative) { result = current; - return curIndex; + return currentIndex; } } - else if (currentAbs > curMaxAbs) + else if (currentAbs > resultAbs) { result = current; - return curIndex; + return currentIndex; } return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(Vector result, Vector maxIndex) + public int Invoke(Vector current, Vector currentIndex) { - float curMax = result[0]; - int curIn = maxIndex[0]; + float result = current[0]; + float resultAbs = MathF.Abs(result); + int resultIndex = currentIndex[0]; + for (int i = 1; i < Vector.Count; i++) { - if (MathF.Abs(result[i]) == MathF.Abs(curMax) && IsNegative(curMax) && !IsNegative(result[i])) + float currentAbs = MathF.Abs(current[i]); + + if (resultAbs == currentAbs) { - curMax = result[i]; - curIn = maxIndex[i]; + bool resultNegative = IsNegative(result); + if ((resultNegative == IsNegative(current[i])) ? (currentIndex[i] < resultIndex) : resultNegative) + { + result = current[i]; + resultAbs = currentAbs; + resultIndex = currentIndex[i]; + } } - else if (MathF.Abs(result[i]) > MathF.Abs(curMax)) + else if (currentAbs > resultAbs) { - curMax = result[i]; - curIn = maxIndex[i]; + result = current[i]; + resultAbs = currentAbs; + resultIndex = currentIndex[i]; } } - return curIn; + return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector curIndex) + public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector currentIndex) { - Vector maxMag = Vector.Abs(result), currentMag = Vector.Abs(current); - - Vector lessThanMask = Vector.GreaterThan(maxMag, currentMag); - - Vector equalMask = Vector.Equals(result, current); + Vector resultMag = Vector.Abs(result), currentMag = Vector.Abs(current); + Vector useResult = Vector.GreaterThan(resultMag, currentMag); + Vector equalMask = Vector.Equals(resultMag, currentMag); if (equalMask != Vector.Zero) { - Vector negativeMask = IsNegative(current); - Vector lessThanIndexMask = Vector.LessThan(resultIndex, curIndex); - - lessThanMask |= ((Vector)~negativeMask & equalMask) | ((Vector)IsNegative(result) & equalMask & lessThanIndexMask); + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(current)); + Vector currentNegative = IsNegative(current); + useResult |= + equalMask & + Vector.ConditionalSelect(Vector.Equals(IsNegative(result), currentNegative), + Vector.LessThan(resultIndex, currentIndex), + (Vector)currentNegative); } - result = Vector.ConditionalSelect(lessThanMask, result, current); - - resultIndex = Vector.ConditionalSelect(lessThanMask, resultIndex, curIndex); + result = Vector.ConditionalSelect(useResult, result, current); + resultIndex = Vector.ConditionalSelect(useResult, resultIndex, currentIndex); } } private readonly struct IndexOfMinOperator_Single : IIndexOfOperator { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(ref float result, float current, int resultIndex, int curIndex) + public int Invoke(ref float result, float current, int resultIndex, int currentIndex) { if (result == current) { - if (IsPositive(result) && !IsPositive(current)) + bool currentNegative = IsNegative(current); + if ((IsNegative(result) == currentNegative) ? (currentIndex < resultIndex) : currentNegative) { result = current; - return curIndex; + return currentIndex; } } else if (current < result) { result = current; - return curIndex; + return currentIndex; } return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(Vector result, Vector resultIndex) + public int Invoke(Vector current, Vector currentIndex) { - float curMin = result[0]; - int curIn = resultIndex[0]; + float result = current[0]; + int resultIndex = currentIndex[0]; + for (int i = 1; i < Vector.Count; i++) { - if (result[i] == curMin && IsPositive(curMin) && !IsPositive(result[i])) + if (current[i] == result) { - curMin = result[i]; - curIn = resultIndex[i]; + bool currentNegative = IsNegative(current[i]); + if ((IsNegative(result) == currentNegative) ? (currentIndex[i] < resultIndex) : currentNegative) + { + result = current[i]; + resultIndex = currentIndex[i]; + } } - else if (result[i] < curMin) + else if (current[i] < result) { - curMin = result[i]; - curIn = resultIndex[i]; + result = current[i]; + resultIndex = currentIndex[i]; } } - return curIn; + return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector curIndex) + public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector currentIndex) { - Vector lessThanMask = Vector.LessThan(result, current); - + Vector useResult = Vector.LessThan(result, current); Vector equalMask = Vector.Equals(result, current); if (equalMask != Vector.Zero) { - Vector negativeMask = IsNegative(current); - Vector lessThanIndexMask = Vector.LessThan(resultIndex, curIndex); - - lessThanMask |= ((Vector)negativeMask & equalMask) | (~(Vector)IsNegative(result) & equalMask & lessThanIndexMask); + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector resultNegative = IsNegative(result); + useResult |= + equalMask & + Vector.ConditionalSelect(Vector.Equals(resultNegative, IsNegative(current)), + Vector.LessThan(resultIndex, currentIndex), + (Vector)resultNegative); } - result = Vector.ConditionalSelect(lessThanMask, result, current); - - resultIndex = Vector.ConditionalSelect(lessThanMask, resultIndex, curIndex); + result = Vector.ConditionalSelect(useResult, result, current); + resultIndex = Vector.ConditionalSelect(useResult, resultIndex, currentIndex); } } private readonly struct IndexOfMinMagnitudeOperator_Single : IIndexOfOperator { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(ref float result, float current, int resultIndex, int curIndex) + public int Invoke(ref float result, float current, int resultIndex, int currentIndex) { - float curMinAbs = MathF.Abs(result); + float resultAbs = MathF.Abs(result); float currentAbs = MathF.Abs(current); - if (curMinAbs == currentAbs) + + if (resultAbs == currentAbs) { - if (IsPositive(result) && !IsPositive(current)) + bool currentNegative = IsNegative(current); + if ((IsNegative(result) == currentNegative) ? (currentIndex < resultIndex) : currentNegative) { result = current; - return curIndex; + return currentIndex; } } - else if (currentAbs < curMinAbs) + else if (currentAbs < resultAbs) { result = current; - return curIndex; + return currentIndex; } return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Invoke(Vector result, Vector resultIndex) + public int Invoke(Vector current, Vector currentIndex) { - float curMin = result[0]; - int curIn = resultIndex[0]; + float result = current[0]; + float resultAbs = MathF.Abs(result); + int resultIndex = currentIndex[0]; + for (int i = 1; i < Vector.Count; i++) { - if (MathF.Abs(result[i]) == MathF.Abs(curMin) && IsPositive(curMin) && !IsPositive(result[i])) + float currentAbs = MathF.Abs(current[i]); + + if (resultAbs == currentAbs) { - curMin = result[i]; - curIn = resultIndex[i]; + bool currentNegative = IsNegative(current[i]); + if ((IsNegative(result) == currentNegative) ? (currentIndex[i] < resultIndex) : currentNegative) + { + result = current[i]; + resultAbs = currentAbs; + resultIndex = currentIndex[i]; + } } - else if (MathF.Abs(result[i]) < MathF.Abs(curMin)) + else if (currentAbs < resultAbs) { - curMin = result[i]; - curIn = resultIndex[i]; + result = current[i]; + resultAbs = currentAbs; + resultIndex = currentIndex[i]; } } - return curIn; + return resultIndex; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector curIndex) + public void Invoke(ref Vector result, Vector current, ref Vector resultIndex, Vector currentIndex) { - Vector minMag = Vector.Abs(result), currentMag = Vector.Abs(current); - - Vector lessThanMask = Vector.LessThan(minMag, currentMag); - - Vector equalMask = Vector.Equals(result, current); + Vector resultMag = Vector.Abs(result), currentMag = Vector.Abs(current); + Vector useResult = Vector.LessThan(resultMag, currentMag); + Vector equalMask = Vector.Equals(resultMag, currentMag); if (equalMask != Vector.Zero) { - Vector negativeMask = IsNegative(current); - Vector lessThanIndexMask = Vector.LessThan(resultIndex, curIndex); - - lessThanMask |= ((Vector)negativeMask & equalMask) | (~(Vector)IsNegative(result) & equalMask & lessThanIndexMask); + // bool useResult = equal && ((IsNegative(result) == IsNegative(current)) ? (resultIndex < currentIndex) : IsNegative(result)); + Vector resultNegative = IsNegative(result); + useResult |= + equalMask & + Vector.ConditionalSelect(Vector.Equals(resultNegative, IsNegative(current)), + Vector.LessThan(resultIndex, currentIndex), + (Vector)resultNegative); } - result = Vector.ConditionalSelect(lessThanMask, result, current); - - resultIndex = Vector.ConditionalSelect(lessThanMask, resultIndex, curIndex); + result = Vector.ConditionalSelect(useResult, result, current); + resultIndex = Vector.ConditionalSelect(useResult, resultIndex, currentIndex); } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs index 272991aed44ab8..380add4727e8d2 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/ThrowHelper.cs @@ -9,7 +9,11 @@ internal static class ThrowHelper { [DoesNotReturn] public static void ThrowArgument_DestinationTooShort() => - throw new ArgumentException(SR.Argument_DestinationTooShort, "destination"); + ThrowArgument_DestinationTooShort("destination"); + + [DoesNotReturn] + public static void ThrowArgument_DestinationTooShort(string destinationName) => + throw new ArgumentException(SR.Argument_DestinationTooShort, destinationName); [DoesNotReturn] public static void ThrowArgument_SpansMustHaveSameLength() => diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs index de0269ca889246..65c45de4c42556 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs @@ -8,6 +8,11 @@ using Xunit; using Xunit.Sdk; +// Tests specific to .NET Core generic APIs. +// Some of the tests are written with functionality abstracted into helpers that provide the core operation: this +// is done when the tests are shared with legacy float-specific tests. Tests that don't need to be shared access +// the generic APIs directly. + namespace System.Numerics.Tensors.Tests { public class DoubleGenericTensorPrimitives : GenericFloatingPointNumberTensorPrimitivesTests { } @@ -30,6 +35,7 @@ public class UInt16GenericTensorPrimitives : GenericIntegerTensorPrimitivesTests public class CharGenericTensorPrimitives : GenericIntegerTensorPrimitivesTests { } public class UInt32GenericTensorPrimitives : GenericIntegerTensorPrimitivesTests { } public class UInt64GenericTensorPrimitives : GenericIntegerTensorPrimitivesTests { } + public class UIntPtrGenericTensorPrimitives : GenericIntegerTensorPrimitivesTests { } public class UInt128GenericTensorPrimitives : GenericIntegerTensorPrimitivesTests { } @@ -113,11 +119,953 @@ protected override void SetSpecialValues(Span x, Span y) x[pos] = T.Epsilon; y[pos] = -T.Epsilon; - // Same magnitude, opposite sign - pos = Random.Next(x.Length); - x[pos] = T.CreateTruncating(5); - y[pos] = T.CreateTruncating(-5); + // Same magnitude, opposite sign + pos = Random.Next(x.Length); + x[pos] = T.CreateTruncating(5); + y[pos] = T.CreateTruncating(-5); + } + + #region Span -> Destination + public static IEnumerable SpanDestinationFunctionsToTest() + { + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Acosh), new Func(T.Acosh) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.AcosPi), new Func(T.AcosPi) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Acos), new Func(T.Acos) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Asinh), new Func(T.Asinh) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.AsinPi), new Func(T.AsinPi) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Asin), new Func(T.Asin) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Atanh), new Func(T.Atanh) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.AtanPi), new Func(T.AtanPi) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Atan), new Func(T.Atan) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Cbrt), new Func(T.Cbrt) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Ceiling), new Func(T.Ceiling) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Cos), new Func(T.Cos) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Cosh), new Func(T.Cosh) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.CosPi), new Func(T.CosPi) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.DegreesToRadians), new Func(T.DegreesToRadians) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Exp), new Func(T.Exp) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Exp2), new Func(T.Exp2) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Exp10), new Func(T.Exp10) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.ExpM1), new Func(T.ExpM1) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Exp2M1), new Func(T.Exp2M1) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Exp10M1), new Func(T.Exp10M1) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Floor), new Func(T.Floor) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Log), new Func(T.Log) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Log2), new Func(T.Log2) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Log10), new Func(T.Log10) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.LogP1), new Func(T.LogP1) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Log2P1), new Func(T.Log2P1) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Log10P1), new Func(T.Log10P1) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.RadiansToDegrees), new Func(T.RadiansToDegrees) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Reciprocal), new Func(f => T.One / f) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.ReciprocalEstimate), new Func(T.ReciprocalEstimate) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.ReciprocalSqrt), new Func(f => T.One / T.Sqrt(f)) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.ReciprocalSqrtEstimate), new Func(T.ReciprocalSqrtEstimate) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Round), new Func(T.Round) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Sin), new Func(T.Sin) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Sinh), new Func(T.Sinh) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.SinPi), new Func(T.SinPi) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Sqrt), new Func(T.Sqrt) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Tan), new Func(T.Tan) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Tanh), new Func(T.Tanh) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.TanPi), new Func(T.TanPi) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.Truncate), new Func(T.Truncate) }; + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_AllLengths(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x.Span, destination.Span); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_InPlace(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x.Span, x.Span); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i]), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_SpecialValues(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + RunForEachSpecialValue(() => + { + tensorPrimitivesMethod(x.Span, destination.Span); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i]), destination[i]); + } + }, x); + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_ValueRange(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(VectorLengthAndIteratedRange(ConvertFromSingle(-100f), ConvertFromSingle(100f), ConvertFromSingle(3f)), arg => + { + T[] x = new T[arg.Length]; + T[] dest = new T[arg.Length]; + + x.AsSpan().Fill(arg.Element); + tensorPrimitivesMethod(x.AsSpan(), dest.AsSpan()); + + T expected = expectedMethod(arg.Element); + foreach (T actual in dest) + { + AssertEqualTolerance(expected, actual); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_ThrowsForTooShortDestination(SpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x.Span, destination.Span)); + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_ThrowsForOverlapppingInputsWithOutputs(SpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(2, 2))); + } + #endregion + + #region Span,Span -> Destination + public static IEnumerable SpanSpanDestinationFunctionsToTest() + { + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Atan2), new Func(T.Atan2) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Atan2Pi), new Func(T.Atan2Pi) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.CopySign), new Func(T.CopySign) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Hypot), new Func(T.Hypot) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Ieee754Remainder), new Func(T.Ieee754Remainder) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Log), new Func(T.Log) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Pow), new Func(T.Pow) }; + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_AllLengths(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_InPlace(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, x, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], xOrig[i]), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_ThrowsForMismatchedLengths(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => tensorPrimitivesMethod(x, y, destination)); + Assert.Throws(() => tensorPrimitivesMethod(y, x, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_ThrowsForTooShortDestination(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_ThrowsForOverlapppingInputsWithOutputs(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(2, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(4, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(6, 2))); + } + #endregion + + #region Span,Scalar -> Destination + public static IEnumerable SpanScalarDestinationFunctionsToTest() + { + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Atan2), new Func(T.Atan2) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Atan2Pi), new Func(T.Atan2Pi) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.CopySign), new Func(T.CopySign) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Ieee754Remainder), new Func(T.Ieee754Remainder) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Pow), new Func(T.Pow) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Log), new Func(T.Log) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Max), new Func(T.Max) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.MaxMagnitude), new Func(T.MaxMagnitude) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Min), new Func(T.Min) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.MinMagnitude), new Func(T.MinMagnitude) }; + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_AllLengths(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_InPlace(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, y, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], y), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_ThrowsForTooShortDestination(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_ThrowsForOverlapppingInputsWithOutputs(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(2, 2))); + } + #endregion + + #region Scalar,Span -> Destination + public static IEnumerable ScalarSpanFloatDestinationFunctionsToTest() + { + yield return new object[] { new ScalarSpanDestinationDelegate(TensorPrimitives.Atan2), new Func(T.Atan2) }; + yield return new object[] { new ScalarSpanDestinationDelegate(TensorPrimitives.Atan2Pi), new Func(T.Atan2Pi) }; + yield return new object[] { new ScalarSpanDestinationDelegate(TensorPrimitives.Pow), new Func(T.Pow) }; + yield return new object[] { new ScalarSpanDestinationDelegate(TensorPrimitives.Ieee754Remainder), new Func(T.Ieee754Remainder) }; + } + + [Theory] + [MemberData(nameof(ScalarSpanFloatDestinationFunctionsToTest))] + public void SpanScalarFloatDestination_AllLengths(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + T x = NextRandom(); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x, y[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(ScalarSpanFloatDestinationFunctionsToTest))] + public void SpanScalarFloatDestination_InPlace(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + T x = NextRandom(); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + T[] yOrig = y.Span.ToArray(); + + tensorPrimitivesMethod(x, y, y); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x, yOrig[i]), y[i]); + } + }); + } + + [Theory] + [MemberData(nameof(ScalarSpanFloatDestinationFunctionsToTest))] + public void SpanScalarFloatDestination_ThrowsForTooShortDestination(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + T x = NextRandom(); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(ScalarSpanFloatDestinationFunctionsToTest))] + public void SpanScalarFloatDestination_ThrowsForOverlapppingInputsWithOutputs(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(default, array.AsSpan(1, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(default, array.AsSpan(1, 2), array.AsSpan(2, 2))); + } + #endregion + + #region Span,Int,Span -> Destination + public static IEnumerable SpanIntDestinationFunctionsToTest() + { + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.RootN), new Func(T.RootN) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.ScaleB), new Func(T.ScaleB) }; + } + + [Theory] + [MemberData(nameof(SpanIntDestinationFunctionsToTest))] + public void SpanIntDestination_AllLengths(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + int y = Random.Next(1, 10); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanIntDestinationFunctionsToTest))] + public void SpanIntDestination_InPlace(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + int y = Random.Next(1, 10); + + tensorPrimitivesMethod(x, y, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], y), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanIntDestinationFunctionsToTest))] + public void SpanIntDestination_ThrowsForTooShortDestination(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + int y = 2; + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanIntDestinationFunctionsToTest))] + public void SpanIntDestination_ThrowsForOverlapppingInputsWithOutputs(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), 2, array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), 2, array.AsSpan(2, 2))); + } + #endregion + + #region Span,Span,Span -> Destination + public static IEnumerable SpanSpanSpanDestinationFunctionsToTest() + { + yield return new object[] { new SpanSpanSpanDestinationDelegate(TensorPrimitives.Lerp), new Func(T.Lerp) }; + yield return new object[] { new SpanSpanSpanDestinationDelegate(TensorPrimitives.FusedMultiplyAdd), new Func(T.FusedMultiplyAdd) }; + } + + [Theory] + [MemberData(nameof(SpanSpanSpanDestinationFunctionsToTest))] + public void SpanSpanSpanDestination_AllLengths(SpanSpanSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory z = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, z, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y[i], z[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanSpanDestinationFunctionsToTest))] + public void SpanSpanSpanDestination_InPlace(SpanSpanSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, x, x, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], xOrig[i], xOrig[i]), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanSpanDestinationFunctionsToTest))] + public void SpanSpanSpanDestination_ThrowsForMismatchedLengths(SpanSpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory z = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => tensorPrimitivesMethod(x, y, z, destination)); + Assert.Throws(() => tensorPrimitivesMethod(x, z, y, destination)); + Assert.Throws(() => tensorPrimitivesMethod(y, x, z, destination)); + Assert.Throws(() => tensorPrimitivesMethod(y, z, x, destination)); + Assert.Throws(() => tensorPrimitivesMethod(z, x, y, destination)); + Assert.Throws(() => tensorPrimitivesMethod(z, y, x, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanSpanDestinationFunctionsToTest))] + public void SpanSpanSpanDestination_ThrowsForTooShortDestination(SpanSpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory z = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, z, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanSpanDestinationFunctionsToTest))] + public void SpanSpanSpanDestination_ThrowsForOverlapppingInputsWithOutputs(SpanSpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(7, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(7, 2), array.AsSpan(2, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(7, 2), array.AsSpan(4, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(7, 2), array.AsSpan(6, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(7, 2), array.AsSpan(8, 2))); + } + #endregion + + #region Span,Span,Scalar -> Destination + public static IEnumerable SpanSpanScalarDestinationFunctionsToTest() + { + yield return new object[] { new SpanSpanScalarDestinationDelegate(TensorPrimitives.FusedMultiplyAdd), new Func(T.FusedMultiplyAdd) }; + yield return new object[] { new SpanSpanScalarDestinationDelegate(TensorPrimitives.Lerp), new Func(T.Lerp) }; + } + + [Theory] + [MemberData(nameof(SpanSpanScalarDestinationFunctionsToTest))] + public void SpanSpanScalarDestination_AllLengths(SpanSpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + T z = NextRandom(); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, z, destination); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y[i], z), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanScalarDestinationFunctionsToTest))] + public void SpanSpanScalarDestination_InPlace(SpanSpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + T z = NextRandom(); + + tensorPrimitivesMethod(x, x, z, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], xOrig[i], z), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanScalarDestinationFunctionsToTest))] + public void SpanSpanScalarDestination_ThrowsForTooShortDestination(SpanSpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + T z = NextRandom(); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, z, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanScalarDestinationFunctionsToTest))] + public void SpanSpanScalarDestination_ThrowsForOverlapppingInputsWithOutputs(SpanSpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(4, 2), default, array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(4, 2), default, array.AsSpan(2, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(4, 2), default, array.AsSpan(3, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(4, 2), default, array.AsSpan(5, 2))); + } + #endregion + + #region Span,Scalar,Span -> Destination + public static IEnumerable SpanScalarSpanDestinationFunctionsToTest() + { + yield return new object[] { new SpanScalarSpanDestinationDelegate(TensorPrimitives.FusedMultiplyAdd), new Func(T.FusedMultiplyAdd) }; + yield return new object[] { new SpanScalarSpanDestinationDelegate(TensorPrimitives.Lerp), new Func(T.Lerp) }; + } + + [Theory] + [MemberData(nameof(SpanScalarSpanDestinationFunctionsToTest))] + public void SpanScalarSpanDestination_AllLengths(SpanScalarSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + using BoundedMemory z = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, z, destination); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y, z[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanScalarSpanDestinationFunctionsToTest))] + public void SpanScalarSpanDestination_InPlace(SpanScalarSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + T y = NextRandom(); + + tensorPrimitivesMethod(x, y, x, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], y, xOrig[i]), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanScalarSpanDestinationFunctionsToTest))] + public void SpanScalarSpanDestination_ThrowsForTooShortDestination(SpanScalarSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + using BoundedMemory z = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, z, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanScalarSpanDestinationFunctionsToTest))] + public void SpanScalarSpanDestination_ThrowsForOverlapppingInputsWithOutputs(SpanScalarSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(4, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(4, 2), array.AsSpan(2, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(4, 2), array.AsSpan(3, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(4, 2), array.AsSpan(5, 2))); + } + #endregion + + #region Span -> Destination,Destination + public static IEnumerable SpanDestinationDestinationFunctionsToTest() + { + yield return new object[] { new SpanDestinationDestinationDelegate(TensorPrimitives.SinCos), new Func(T.SinCos) }; + yield return new object[] { new SpanDestinationDestinationDelegate(TensorPrimitives.SinCosPi), new Func(T.SinCosPi) }; + } + + [Theory] + [MemberData(nameof(SpanDestinationDestinationFunctionsToTest))] + public void SpanDestinationDestinationFunctions_AllLengths(SpanDestinationDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination1 = CreateTensor(tensorLength); + using BoundedMemory destination2 = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x.Span, destination1.Span, destination2.Span); + + for (int i = 0; i < tensorLength; i++) + { + (T expected1, T expected2) = expectedMethod(x[i]); + AssertEqualTolerance(expected1, destination1[i]); + AssertEqualTolerance(expected2, destination2[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationDestinationFunctionsToTest))] + public void SpanDestinationDestinationFunctions_InPlace(SpanDestinationDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + using BoundedMemory destination2 = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x.Span, x.Span, destination2.Span); + + for (int i = 0; i < tensorLength; i++) + { + (T expected1, T expected2) = expectedMethod(xOrig[i]); + AssertEqualTolerance(expected1, x[i]); + AssertEqualTolerance(expected2, destination2[i]); + } + }); + + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + using BoundedMemory destination1 = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x.Span, destination1.Span, x.Span); + + for (int i = 0; i < tensorLength; i++) + { + (T expected1, T expected2) = expectedMethod(xOrig[i]); + AssertEqualTolerance(expected1, destination1[i]); + AssertEqualTolerance(expected2, x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationDestinationFunctionsToTest))] + public void SpanDestinationDestinationFunctions_SpecialValues(SpanDestinationDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination1 = CreateTensor(tensorLength); + using BoundedMemory destination2 = CreateTensor(tensorLength); + + RunForEachSpecialValue(() => + { + tensorPrimitivesMethod(x.Span, destination1.Span, destination2.Span); + for (int i = 0; i < tensorLength; i++) + { + (T expected1, T expected2) = expectedMethod(x[i]); + AssertEqualTolerance(expected1, destination1[i]); + AssertEqualTolerance(expected2, destination2[i]); + } + }, x); + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationDestinationFunctionsToTest))] + public void SpanDestinationDestinationFunctions_ValueRange(SpanDestinationDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(VectorLengthAndIteratedRange(ConvertFromSingle(-100f), ConvertFromSingle(100f), ConvertFromSingle(3f)), arg => + { + T[] x = new T[arg.Length]; + T[] dest1 = new T[arg.Length]; + T[] dest2 = new T[arg.Length]; + + x.AsSpan().Fill(arg.Element); + tensorPrimitivesMethod(x.AsSpan(), dest1.AsSpan(), dest2.AsSpan()); + + (T expected1, T expected2) = expectedMethod(arg.Element); + foreach (T actual in dest1) + { + AssertEqualTolerance(expected1, actual); + } + foreach (T actual in dest2) + { + AssertEqualTolerance(expected2, actual); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationDestinationFunctionsToTest))] + public void SpanDestinationDestinationFunctions_ThrowsForTooShortDestination(SpanDestinationDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination1 = CreateTensor(tensorLength - 1); + using BoundedMemory destination2 = CreateTensor(tensorLength); + + Assert.Throws(() => tensorPrimitivesMethod(x.Span, destination1.Span, destination2.Span)); + }); + + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination1 = CreateTensor(tensorLength); + using BoundedMemory destination2 = CreateTensor(tensorLength - 1); + + Assert.Throws(() => tensorPrimitivesMethod(x.Span, destination1.Span, destination2.Span)); + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationDestinationFunctionsToTest))] + public void SpanDestinationDestinationFunctions_ThrowsForOverlapppingInputsWithOutputs(SpanDestinationDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + Assert.Throws(() => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(0, 2), array.AsSpan(4, 2))); + Assert.Throws(() => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(2, 2), array.AsSpan(4, 2))); + Assert.Throws(() => tensorPrimitivesMethod(array.AsSpan(3, 2), array.AsSpan(0, 2), array.AsSpan(4, 2))); + Assert.Throws(() => tensorPrimitivesMethod(array.AsSpan(5, 2), array.AsSpan(0, 2), array.AsSpan(4, 2))); + } + #endregion + + #region ILogB + [Fact] + public void ILogB_AllLengths() + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = BoundedMemory.Allocate(tensorLength); + + TensorPrimitives.ILogB(x.Span, destination.Span); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.CreateTruncating(T.ILogB(x[i])), T.CreateTruncating(destination[i])); + } + }); + } + + [Fact] + public void ILogB_SpecialValues() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = BoundedMemory.Allocate(tensorLength); + + RunForEachSpecialValue(() => + { + TensorPrimitives.ILogB(x.Span, destination.Span); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.CreateTruncating(T.ILogB(x[i])), T.CreateTruncating(destination[i])); + } + }, x); + }); + } + + [Fact] + public void ILogB_ThrowsForTooShortDestination() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = BoundedMemory.Allocate(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.ILogB(x.Span, destination.Span)); + }); + } + #endregion + + #region Round + public static IEnumerable RoundData() + { + foreach (MidpointRounding mode in Enum.GetValues(typeof(MidpointRounding))) + { + foreach (int digits in new[] { 0, 1, 4 }) + { + yield return new object[] { mode, digits }; + } + } + } + + [Theory] + [MemberData(nameof(RoundData))] + public void Round_AllLengths(MidpointRounding mode, int digits) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + if (digits == 0) + { + if (mode == MidpointRounding.ToEven) + { + TensorPrimitives.Round(x.Span, destination.Span); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.Round(x[i]), destination[i]); + } + } + + TensorPrimitives.Round(x.Span, mode, destination.Span); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.Round(x[i], mode), destination[i]); + } + } + + if (mode == MidpointRounding.ToEven) + { + TensorPrimitives.Round(x.Span, digits, destination.Span); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.Round(x[i], digits), destination[i]); + } + } + + TensorPrimitives.Round(x.Span, digits, mode, destination.Span); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.Round(x[i], digits, mode), destination[i]); + } + }); } + #endregion } public unsafe abstract class GenericSignedIntegerTensorPrimitivesTests : GenericIntegerTensorPrimitivesTests @@ -134,7 +1082,7 @@ public void Abs_MinValue_Throws() FillTensor(x.Span, T.MinValue); x[^1] = T.MinValue; - Assert.Throws(() => Abs(x, destination)); + Assert.Throws(() => TensorPrimitives.Abs(x.Span, destination.Span)); }); } @@ -148,7 +1096,7 @@ public void SumOfMagnitudes_MinValue_Throws() FillTensor(x.Span, T.MinValue); x[^1] = T.MinValue; - Assert.Throws(() => SumOfMagnitudes(x)); + Assert.Throws(() => TensorPrimitives.SumOfMagnitudes(x.Span)); }); } } @@ -168,8 +1116,7 @@ public void Divide_TwoTensors_ByZero_Throws() FillTensor(y.Span, T.Zero); y[^1] = T.Zero; - Exception e = Record.Exception(() => Divide(x, y, destination)); - Assert.True(e is DivideByZeroException or ArgumentOutOfRangeException); // TODO https://github.com/dotnet/runtime/issues/94593: Fix exception type + Assert.Throws(() => TensorPrimitives.Divide(x.Span, y.Span, destination.Span)); }); } @@ -181,10 +1128,420 @@ public void Divide_TensorScalar_ByZero_Throw() using BoundedMemory x = CreateAndFillTensor(tensorLength); using BoundedMemory destination = CreateTensor(tensorLength); - Exception e = Record.Exception(() => Divide(x, T.Zero, destination)); - Assert.True(e is DivideByZeroException or ArgumentOutOfRangeException); // TODO https://github.com/dotnet/runtime/issues/94593: Fix exception type + Assert.Throws(() => TensorPrimitives.Divide(x, T.Zero, destination)); + }); + } + + [Fact] + public void Divide_ScalarTensor_ByZero_Throw() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + x[Random.Next(x.Length)] = T.Zero; + + Assert.Throws(() => TensorPrimitives.Divide(T.One, x, destination)); + }); + } + + #region Span -> Destination + public static IEnumerable SpanDestinationFunctionsToTest() + { + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.OnesComplement), new Func(i => ~i) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.PopCount), new Func(T.PopCount) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.LeadingZeroCount), new Func(T.LeadingZeroCount) }; + yield return new object[] { new SpanDestinationDelegate(TensorPrimitives.TrailingZeroCount), new Func(T.TrailingZeroCount) }; + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_AllLengths(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, destination); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_InPlace(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i]), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_ThrowsForTooShortDestination(SpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanDestinationFunctionsToTest))] + public void SpanDestinationFunctions_ThrowsForOverlapppingInputsWithOutputs(SpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(2, 2))); + } + #endregion + + #region Span,Span -> Destination + public static IEnumerable SpanSpanDestinationFunctionsToTest() + { + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.BitwiseAnd), new Func((x, y) => x & y) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.BitwiseOr), new Func((x, y) => x | y) }; + yield return new object[] { new SpanSpanDestinationDelegate(TensorPrimitives.Xor), new Func((x, y) => x ^ y) }; + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_AllLengths(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_InPlace(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, x, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], xOrig[i]), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_ThrowsForMismatchedLengths(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => tensorPrimitivesMethod(x, y, destination)); + Assert.Throws(() => tensorPrimitivesMethod(y, x, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_ThrowsForTooShortDestination(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanSpanDestinationFunctionsToTest))] + public void SpanSpanDestination_ThrowsForOverlapppingInputsWithOutputs(SpanSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(2, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(4, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(6, 2))); + } + #endregion + + #region Span,Scalar -> Destination + public static IEnumerable SpanScalarDestinationFunctionsToTest() + { + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.BitwiseAnd), new Func((x, y) => x & y) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.BitwiseOr), new Func((x, y) => x | y) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Max), new Func(T.Max) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.MaxMagnitude), new Func(T.MaxMagnitude) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Min), new Func(T.Min) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.MinMagnitude), new Func(T.MinMagnitude) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.Xor), new Func((x, y) => x ^ y) }; + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_AllLengths(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_InPlace(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, y, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], y), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_ThrowsForTooShortDestination(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + T y = NextRandom(); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(SpanScalarDestinationFunctionsToTest))] + public void SpanScalarDestination_ThrowsForOverlapppingInputWithOutputs(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + T y = NextRandom(); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), y, array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), y, array.AsSpan(2, 2))); + } + #endregion + + #region Shifting/Rotating + public static IEnumerable ShiftRotateDestinationFunctionsToTest() + { + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.ShiftLeft), new Func((x, n) => x << n) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.ShiftRightArithmetic), new Func((x, n) => x >> n) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.ShiftRightLogical), new Func((x, n) => x >>> n) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.RotateLeft), new Func(T.RotateLeft) }; + yield return new object[] { new SpanScalarDestinationDelegate(TensorPrimitives.RotateRight), new Func(T.RotateRight) }; + } + + [Theory] + [MemberData(nameof(ShiftRotateDestinationFunctionsToTest))] + public void ShiftRotateDestination_AllLengths(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + int y = Random.Next(0, T.MaxValue.GetByteCount() * 8); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x[i], y), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(ShiftRotateDestinationFunctionsToTest))] + public void ShiftRotateDestination_InPlace(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + int y = Random.Next(0, T.MaxValue.GetByteCount() * 8); + T[] xOrig = x.Span.ToArray(); + + tensorPrimitivesMethod(x, y, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(xOrig[i], y), x[i]); + } + }); + } + + [Theory] + [MemberData(nameof(ShiftRotateDestinationFunctionsToTest))] + public void ShiftRotateDestination_ThrowsForTooShortDestination(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, default, destination)); + }); + } + + [Theory] + [MemberData(nameof(ShiftRotateDestinationFunctionsToTest))] + public void ShiftRotateDestination_ThrowsForOverlapppingInputWithOutputs(SpanScalarDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(array.AsSpan(1, 2), default, array.AsSpan(2, 2))); + } + #endregion + + #region CopySign + private void RemoveSignedMinValue(Span span) + { + for (int i = 0; i < span.Length; i++) + { + while (T.Sign(span[i]) < 0 && span[i] == T.MinValue) + { + span[i] = NextRandom(); + } + } + } + + [Fact] + public void CopySign_AllLengths() + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + RemoveSignedMinValue(x); // CopySign doesn't work with MinValue for signed integers, so remove any MinValue values from the input. + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.CopySign(x, y, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.CopySign(x[i], y[i]), destination[i]); + } + + if (tensorLength > 0) + { + TensorPrimitives.CopySign(x, y[0], destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.CopySign(x[i], y[0]), destination[i]); + } + } + }); + } + + [Fact] + public void CopySign_InPlace() + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + RemoveSignedMinValue(x); // CopySign doesn't work with MinValue for signed integers, so remove any MinValue values from the input. + + T[] xOrig = x.Span.ToArray(); + + TensorPrimitives.CopySign(x, x, x); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(T.CopySign(xOrig[i], xOrig[i]), x[i]); + } + }); + } + + [Fact] + public void CopySign_ThrowsForMismatchedLengths() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => TensorPrimitives.CopySign(x, y, destination)); + Assert.Throws(() => TensorPrimitives.CopySign(y, x, destination)); + }); + } + + [Fact] + public void CopySign_ThrowsForTooShortDestination() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(x, y, destination)); + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(x, y[0], destination)); }); } + + [Fact] + public void CopySign_ThrowsForOverlapppingInputsWithOutputs() + { + T[] array = new T[10]; + + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(2, 2))); + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(4, 2))); + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(array.AsSpan(1, 2), array.AsSpan(5, 2), array.AsSpan(6, 2))); + + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(array.AsSpan(1, 2), default(T), array.AsSpan(0, 2))); + AssertExtensions.Throws("destination", () => TensorPrimitives.CopySign(array.AsSpan(1, 2), default(T), array.AsSpan(2, 2))); + } + #endregion } public unsafe abstract class GenericNumberTensorPrimitivesTests : TensorPrimitivesTests @@ -203,6 +1560,10 @@ public unsafe abstract class GenericNumberTensorPrimitivesTests : TensorPrimi protected override void Divide(ReadOnlySpan x, T y, Span destination) => TensorPrimitives.Divide(x, y, destination); protected override T Divide(T x, T y) => x / y; protected override T Dot(ReadOnlySpan x, ReadOnlySpan y) => TensorPrimitives.Dot(x, y); + protected override int IndexOfMax(ReadOnlySpan x) => TensorPrimitives.IndexOfMax(x); + protected override int IndexOfMaxMagnitude(ReadOnlySpan x) => TensorPrimitives.IndexOfMaxMagnitude(x); + protected override int IndexOfMin(ReadOnlySpan x) => TensorPrimitives.IndexOfMin(x); + protected override int IndexOfMinMagnitude(ReadOnlySpan x) => TensorPrimitives.IndexOfMinMagnitude(x); protected override T Max(ReadOnlySpan x) => TensorPrimitives.Max(x); protected override void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) => TensorPrimitives.Max(x, y, destination); protected override T Max(T x, T y) => T.Max(x, y); @@ -291,5 +1652,76 @@ protected override void AssertEqualTolerance(T expected, T actual, T tolerance) protected override T NaN => throw new NotSupportedException(); protected override IEnumerable GetSpecialValues() => Enumerable.Empty(); protected override void SetSpecialValues(Span x, Span y) { } + + #region Scalar,Span -> Destination + public static IEnumerable ScalarSpanDestinationFunctionsToTest() + { + yield return new object[] { new ScalarSpanDestinationDelegate(TensorPrimitives.Divide), new Func((x, y) => x / y) }; + yield return new object[] { new ScalarSpanDestinationDelegate(TensorPrimitives.Subtract), new Func((x, y) => x - y) }; + } + + [Theory] + [MemberData(nameof(ScalarSpanDestinationFunctionsToTest))] + public void ScalarSpanDestination_AllLengths(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + T x = NextRandom(); + using BoundedMemory y = CreateTensor(tensorLength); + FillTensor(y.Span, default); + using BoundedMemory destination = CreateTensor(tensorLength); + + tensorPrimitivesMethod(x, y, destination); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x, y[i]), destination[i]); + } + }); + } + + [Theory] + [MemberData(nameof(ScalarSpanDestinationFunctionsToTest))] + public void ScalarSpanDestination_InPlace(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) + { + Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => + { + T x = NextRandom(); + using BoundedMemory y = CreateTensor(tensorLength); + FillTensor(y.Span, default); + T[] yOrig = y.Span.ToArray(); + + tensorPrimitivesMethod(x, y.Span, y.Span); + + for (int i = 0; i < tensorLength; i++) + { + AssertEqualTolerance(expectedMethod(x, yOrig[i]), y[i]); + } + }); + } + + [Theory] + [MemberData(nameof(ScalarSpanDestinationFunctionsToTest))] + public void ScalarSpanDestination_ThrowsForTooShortDestination(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + T x = NextRandom(); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(x, y, destination)); + }); + } + + [Theory] + [MemberData(nameof(ScalarSpanDestinationFunctionsToTest))] + public void ScalarSpanDestination_ThrowsForOverlapppingInputsWithOutputs(ScalarSpanDestinationDelegate tensorPrimitivesMethod, Func _) + { + T[] array = new T[10]; + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(default, array.AsSpan(4, 2), array.AsSpan(3, 2))); + AssertExtensions.Throws("destination", () => tensorPrimitivesMethod(default, array.AsSpan(4, 2), array.AsSpan(5, 2))); + } + #endregion } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs index 50e3b3dae77d42..85fb2b955cceb9 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs @@ -1,13 +1,11 @@ // 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; using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using Xunit; using Xunit.Sdk; +// Tests specific to .NET Standard non-generic APIs + namespace System.Numerics.Tensors.Tests { public unsafe class NonGenericSingleTensorPrimitivesTests : TensorPrimitivesTests @@ -35,6 +33,10 @@ public unsafe class NonGenericSingleTensorPrimitivesTests : TensorPrimitivesTest protected override void Log(ReadOnlySpan x, Span destination) => TensorPrimitives.Log(x, destination); protected override float Log2(float x) => MathF.Log(x, 2); protected override void Log2(ReadOnlySpan x, Span destination) => TensorPrimitives.Log2(x, destination); + protected override int IndexOfMax(ReadOnlySpan x) => TensorPrimitives.IndexOfMax(x); + protected override int IndexOfMaxMagnitude(ReadOnlySpan x) => TensorPrimitives.IndexOfMaxMagnitude(x); + protected override int IndexOfMin(ReadOnlySpan x) => TensorPrimitives.IndexOfMin(x); + protected override int IndexOfMinMagnitude(ReadOnlySpan x) => TensorPrimitives.IndexOfMinMagnitude(x); protected override float Max(ReadOnlySpan x) => TensorPrimitives.Max(x); protected override void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) => TensorPrimitives.Max(x, y, destination); protected override float Max(float x, float y) => MathF.Max(x, y); @@ -174,203 +176,5 @@ protected override void SetSpecialValues(Span x, Span y) } private static unsafe float UInt32ToSingle(uint i) => *(float*)&i; - - // TODO: Move these IndexOf tests to the base class once generic versions are implemented. - #region IndexOfMax - [Fact] - public void IndexOfMax_ReturnsNegative1OnEmpty() - { - Assert.Equal(-1, TensorPrimitives.IndexOfMax(ReadOnlySpan.Empty)); - } - - [Fact] - public void IndexOfMax() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = Enumerable.Max(MemoryMarshal.ToEnumerable(x.Memory)) + 1; - Assert.Equal(expected, TensorPrimitives.IndexOfMax(x)); - } - }); - } - - [Fact] - public void IndexOfMax_FirstNaNReturned() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = float.NaN; - x[tensorLength - 1] = float.NaN; - Assert.Equal(expected, TensorPrimitives.IndexOfMax(x)); - } - }); - } - - [Fact] - public void IndexOfMax_Negative0LesserThanPositive0() - { - Assert.Equal(1, TensorPrimitives.IndexOfMax([-0f, +0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMax([-0f, -0f, -0f, -0f])); - Assert.Equal(4, TensorPrimitives.IndexOfMax([-0f, -0f, -0f, -0f, +0f, +0f, +0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMax([+0f, -0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMax([-1, -0f])); - Assert.Equal(2, TensorPrimitives.IndexOfMax([-1, -0f, 1])); - } - #endregion - - #region IndexOfMaxMagnitude - [Fact] - public void IndexOfMaxMagnitude_ReturnsNegative1OnEmpty() - { - Assert.Equal(-1, TensorPrimitives.IndexOfMaxMagnitude(ReadOnlySpan.Empty)); - } - - [Fact] - public void IndexOfMaxMagnitude() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = Enumerable.Max(MemoryMarshal.ToEnumerable(x.Memory), Math.Abs) + 1; - Assert.Equal(expected, TensorPrimitives.IndexOfMaxMagnitude(x)); - } - }); - } - - [Fact] - public void IndexOfMaxMagnitude_FirstNaNReturned() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = float.NaN; - x[tensorLength - 1] = float.NaN; - Assert.Equal(expected, TensorPrimitives.IndexOfMaxMagnitude(x)); - } - }); - } - - [Fact] - public void IndexOfMaxMagnitude_Negative0LesserThanPositive0() - { - Assert.Equal(0, TensorPrimitives.IndexOfMaxMagnitude([-0f, -0f, -0f, -0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMaxMagnitude([-0f, +0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMaxMagnitude([-0f, +0f, +0f, +0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMaxMagnitude([+0f, -0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMaxMagnitude([-1, -0f])); - Assert.Equal(2, TensorPrimitives.IndexOfMaxMagnitude([-1, -0f, 1])); - } - #endregion - - #region IndexOfMin - [Fact] - public void IndexOfMin_ReturnsNegative1OnEmpty() - { - Assert.Equal(-1, TensorPrimitives.IndexOfMin(ReadOnlySpan.Empty)); - } - - [Fact] - public void IndexOfMin() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = Enumerable.Min(MemoryMarshal.ToEnumerable(x.Memory)) - 1; - Assert.Equal(expected, TensorPrimitives.IndexOfMin(x)); - } - }); - } - - [Fact] - public void IndexOfMin_FirstNaNReturned() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = float.NaN; - x[tensorLength - 1] = float.NaN; - Assert.Equal(expected, TensorPrimitives.IndexOfMin(x)); - } - }); - } - - [Fact] - public void IndexOfMin_Negative0LesserThanPositive0() - { - Assert.Equal(0, TensorPrimitives.IndexOfMin([-0f, +0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMin([+0f, -0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMin([+0f, -0f, -0f, -0f, -0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMin([-1, -0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMin([-1, -0f, 1])); - } - #endregion - - #region IndexOfMinMagnitude - [Fact] - public void IndexOfMinMagnitude_ReturnsNegative1OnEmpty() - { - Assert.Equal(-1, TensorPrimitives.IndexOfMinMagnitude(ReadOnlySpan.Empty)); - } - - [Fact] - public void IndexOfMinMagnitude() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateTensor(tensorLength); - for (int i = 0; i < x.Length; i++) - { - x[i] = i % 2 == 0 ? 42 : -42; - } - - x[expected] = -41; - - Assert.Equal(expected, TensorPrimitives.IndexOfMinMagnitude(x)); - } - }); - } - - [Fact] - public void IndexOfMinMagnitude_FirstNaNReturned() - { - Assert.All(Helpers.TensorLengths, tensorLength => - { - foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) - { - using BoundedMemory x = CreateAndFillTensor(tensorLength); - x[expected] = float.NaN; - x[tensorLength - 1] = float.NaN; - Assert.Equal(expected, TensorPrimitives.IndexOfMinMagnitude(x)); - } - }); - } - - [Fact] - public void IndexOfMinMagnitude_Negative0LesserThanPositive0() - { - Assert.Equal(0, TensorPrimitives.IndexOfMinMagnitude([-0f, -0f, -0f, -0f])); - Assert.Equal(0, TensorPrimitives.IndexOfMinMagnitude([-0f, +0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMinMagnitude([+0f, -0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMinMagnitude([+0f, -0f, -0f, -0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMinMagnitude([-1, -0f])); - Assert.Equal(1, TensorPrimitives.IndexOfMinMagnitude([-1, -0f, 1])); - } - #endregion } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs index 8cc159b5b3b14c..8a9f9ff6b4de29 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs @@ -7,6 +7,8 @@ using System.Runtime.InteropServices; using Xunit; +// Shared helpers and Facts/Theories used by both generic methods on .NET Core and non-generic methods on .NET Framework + namespace System.Numerics.Tensors.Tests { public abstract class TensorPrimitivesTests where T : unmanaged, IEquatable @@ -25,6 +27,10 @@ public abstract class TensorPrimitivesTests where T : unmanaged, IEquatable x, T y, Span destination); protected abstract T Dot(ReadOnlySpan x, ReadOnlySpan y); protected abstract void Exp(ReadOnlySpan x, Span destination); + protected abstract int IndexOfMax(ReadOnlySpan x); + protected abstract int IndexOfMaxMagnitude(ReadOnlySpan x); + protected abstract int IndexOfMin(ReadOnlySpan x); + protected abstract int IndexOfMinMagnitude(ReadOnlySpan x); protected abstract void Log(ReadOnlySpan x, Span destination); protected abstract void Log2(ReadOnlySpan x, Span destination); protected abstract T Max(ReadOnlySpan x); @@ -84,6 +90,16 @@ public abstract class TensorPrimitivesTests where T : unmanaged, IEquatable x, Span destination); + public delegate void SpanSpanDestinationDelegate(ReadOnlySpan x, ReadOnlySpan y, Span destination); + public delegate void SpanScalarDestinationDelegate(ReadOnlySpan x, T2 y, Span destination); + public delegate void ScalarSpanDestinationDelegate(T x, ReadOnlySpan y, Span destination); + public delegate void SpanSpanSpanDestinationDelegate(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan z, Span destination); + public delegate void SpanSpanScalarDestinationDelegate(ReadOnlySpan x, ReadOnlySpan y, T z, Span destination); + public delegate void SpanScalarSpanDestinationDelegate(ReadOnlySpan x, T y, ReadOnlySpan z, Span destination); + public delegate void SpanDestinationDestinationDelegate(ReadOnlySpan x, Span destination1, Span destination2); + protected virtual bool IsFloatingPoint => typeof(T) == typeof(float) || typeof(T) == typeof(double); protected abstract T ConvertFromSingle(float f); @@ -236,15 +252,6 @@ public void Add_TwoTensors() { AssertEqualTolerance(Add(x[i], y[i]), destination[i]); } - - T[] xOrig = x.Span.ToArray(); - - // Validate that the destination can be the same as an input. - Add(x, x, x); - for (int i = 0; i < tensorLength; i++) - { - AssertEqualTolerance(Add(xOrig[i], xOrig[i]), x[i]); - } }); } @@ -1047,6 +1054,265 @@ public void Exp_ThrowsForOverlapppingInputsWithOutputs() } #endregion + #region IndexOfMax + [Fact] + public void IndexOfMax_ReturnsNegative1OnEmpty() + { + Assert.Equal(-1, IndexOfMax(ReadOnlySpan.Empty)); + } + + [Fact] + public void IndexOfMax_AllLengths() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + x[expected] = Enumerable.Max(MemoryMarshal.ToEnumerable(x.Memory)); + int actual = IndexOfMax(x.Span); + Assert.True(actual == expected || (actual < expected && x[actual].Equals(x[expected])), $"{tensorLength} {actual} {expected} {string.Join(",", MemoryMarshal.ToEnumerable(x.Memory))}"); + } + }); + } + + [Fact] + public void IndexOfMax_FirstNaNReturned() + { + if (!IsFloatingPoint) return; + + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + x[expected] = ConvertFromSingle(float.NaN); + x[tensorLength - 1] = ConvertFromSingle(float.NaN); + Assert.Equal(expected, IndexOfMax(x.Span)); + } + }); + } + + [Fact] + public void IndexOfMax_Negative0LesserThanPositive0() + { + if (!IsFloatingPoint) return; + + Assert.Equal(1, IndexOfMax([ConvertFromSingle(-0f), ConvertFromSingle(+0f)])); + Assert.Equal(0, IndexOfMax([ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f)])); + Assert.Equal(4, IndexOfMax([ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(+0f), ConvertFromSingle(+0f), ConvertFromSingle(+0f)])); + Assert.Equal(0, IndexOfMax([ConvertFromSingle(+0f), ConvertFromSingle(-0f)])); + Assert.Equal(1, IndexOfMax([ConvertFromSingle(-1), ConvertFromSingle(-0f)])); + Assert.Equal(2, IndexOfMax([ConvertFromSingle(-1), ConvertFromSingle(-0f), ConvertFromSingle(1f)])); + } + #endregion + + #region IndexOfMaxMagnitude + [Fact] + public void IndexOfMaxMagnitude_ReturnsNegative1OnEmpty() + { + Assert.Equal(-1, IndexOfMaxMagnitude(ReadOnlySpan.Empty)); + } + + [Fact] + public void IndexOfMaxMagnitude_AllLengths() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateTensor(tensorLength); + FillTensor(x, MinValue); + + T max = x[0]; + for (int i = 0; i < x.Length; i++) + { + int compared = Comparer.Default.Compare(Abs(x[i]), Abs(max)); + if (compared > 0 || (compared == 0 && EqualityComparer.Default.Equals(x[i], max))) + { + max = x[i]; + } + } + x[expected] = max; + + int actual = IndexOfMaxMagnitude(x.Span); + + if (actual != expected) + { + Assert.True(actual < expected || Comparer.Default.Compare(x[actual], x[expected]) > 0, $"{tensorLength} {actual} {expected} {string.Join(",", MemoryMarshal.ToEnumerable(x.Memory))}"); + if (IsFloatingPoint) + { + AssertEqualTolerance(Abs(x[expected]), Abs(x[actual])); + } + else + { + Assert.Equal(Abs(x[expected]), Abs(x[actual])); + } + } + } + }); + } + + [Fact] + public void IndexOfMaxMagnitude_FirstNaNReturned() + { + if (!IsFloatingPoint) return; + + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + x[expected] = ConvertFromSingle(float.NaN); + x[tensorLength - 1] = ConvertFromSingle(float.NaN); + Assert.Equal(expected, IndexOfMaxMagnitude(x)); + } + }); + } + + [Fact] + public void IndexOfMaxMagnitude_Negative0LesserThanPositive0() + { + if (!IsFloatingPoint) return; + + Assert.Equal(0, IndexOfMaxMagnitude([ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f)])); + Assert.Equal(1, IndexOfMaxMagnitude([ConvertFromSingle(-0f), ConvertFromSingle(+0f)])); + Assert.Equal(1, IndexOfMaxMagnitude([ConvertFromSingle(-0f), ConvertFromSingle(+0f), ConvertFromSingle(+0f), ConvertFromSingle(+0f)])); + Assert.Equal(0, IndexOfMaxMagnitude([ConvertFromSingle(+0f), ConvertFromSingle(-0f)])); + Assert.Equal(0, IndexOfMaxMagnitude([ConvertFromSingle(-1), ConvertFromSingle(-0f)])); + Assert.Equal(2, IndexOfMaxMagnitude([ConvertFromSingle(-1), ConvertFromSingle(-0f), ConvertFromSingle(1f)])); + } + #endregion + + #region IndexOfMin + [Fact] + public void IndexOfMin_ReturnsNegative1OnEmpty() + { + Assert.Equal(-1, IndexOfMin(ReadOnlySpan.Empty)); + } + + [Fact] + public void IndexOfMin_AllLengths() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + x[expected] = Enumerable.Min(MemoryMarshal.ToEnumerable(x.Memory)); + int actual = IndexOfMin(x.Span); + Assert.True(actual == expected || (actual < expected && x[actual].Equals(x[expected])), $"{tensorLength} {actual} {expected} {string.Join(",", MemoryMarshal.ToEnumerable(x.Memory))}"); + } + }); + } + + [Fact] + public void IndexOfMin_FirstNaNReturned() + { + if (!IsFloatingPoint) return; + + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + x[expected] = ConvertFromSingle(float.NaN); + x[tensorLength - 1] = ConvertFromSingle(float.NaN); + Assert.Equal(expected, IndexOfMin(x)); + } + }); + } + + [Fact] + public void IndexOfMin_Negative0LesserThanPositive0() + { + if (!IsFloatingPoint) return; + + Assert.Equal(0, IndexOfMin([ConvertFromSingle(-0f), ConvertFromSingle(+0f)])); + Assert.Equal(1, IndexOfMin([ConvertFromSingle(+0f), ConvertFromSingle(-0f)])); + Assert.Equal(1, IndexOfMin([ConvertFromSingle(+0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f)])); + Assert.Equal(0, IndexOfMin([ConvertFromSingle(-1), ConvertFromSingle(-0f)])); + Assert.Equal(0, IndexOfMin([ConvertFromSingle(-1), ConvertFromSingle(-0f), ConvertFromSingle(1f)])); + } + #endregion + + #region IndexOfMinMagnitude + [Fact] + public void IndexOfMinMagnitude_ReturnsNegative1OnEmpty() + { + Assert.Equal(-1, IndexOfMinMagnitude(ReadOnlySpan.Empty)); + } + + [Fact] + public void IndexOfMinMagnitude_AllLengths() + { + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateTensor(tensorLength); + FillTensor(x, MinValue); + + T min = x[0]; + for (int i = 0; i < x.Length; i++) + { + int compared = Comparer.Default.Compare(Abs(x[i]), Abs(min)); + if (compared < 0 || (compared == 0 && Comparer.Default.Compare(x[i], min) < 0)) + { + min = x[i]; + } + } + + x[expected] = min; + int actual = IndexOfMinMagnitude(x.Span); + + if (actual != expected) + { + Assert.True(actual < expected || Comparer.Default.Compare(x[actual], x[expected]) < 0, $"{tensorLength} {actual} {expected} {string.Join(",", MemoryMarshal.ToEnumerable(x.Memory))}"); + if (IsFloatingPoint) + { + AssertEqualTolerance(Abs(x[expected]), Abs(x[actual])); + } + else + { + Assert.Equal(Abs(x[expected]), Abs(x[actual])); + } + } + } + }); + } + + [Fact] + public void IndexOfMinMagnitude_FirstNaNReturned() + { + if (!IsFloatingPoint) return; + + Assert.All(Helpers.TensorLengths, tensorLength => + { + foreach (int expected in new[] { 0, tensorLength / 2, tensorLength - 1 }) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + x[expected] = ConvertFromSingle(float.NaN); + x[tensorLength - 1] = ConvertFromSingle(float.NaN); + Assert.Equal(expected, IndexOfMinMagnitude(x)); + } + }); + } + + [Fact] + public void IndexOfMinMagnitude_Negative0LesserThanPositive0() + { + if (!IsFloatingPoint) return; + + Assert.Equal(0, IndexOfMinMagnitude([ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f)])); + Assert.Equal(0, IndexOfMinMagnitude([ConvertFromSingle(-0f), ConvertFromSingle(+0f)])); + Assert.Equal(1, IndexOfMinMagnitude([ConvertFromSingle(+0f), ConvertFromSingle(-0f)])); + Assert.Equal(1, IndexOfMinMagnitude([ConvertFromSingle(+0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f), ConvertFromSingle(-0f)])); + Assert.Equal(1, IndexOfMinMagnitude([ConvertFromSingle(-1), ConvertFromSingle(-0f)])); + Assert.Equal(1, IndexOfMinMagnitude([ConvertFromSingle(-1), ConvertFromSingle(-0f), ConvertFromSingle(1f)])); + } + #endregion + #region Log [Fact] public void Log_AllValues() From 205ef031e0fe5152dede0bd9f99d0f6f9e7f1e45 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 22 Jan 2024 10:31:44 +0100 Subject: [PATCH 155/189] JIT: Remove old loop code (#97232) All dependencies on old loop finding have been removed, so remove all old code associated with it. --- src/coreclr/jit/block.cpp | 10 +- src/coreclr/jit/block.h | 12 +- src/coreclr/jit/compiler.cpp | 127 +- src/coreclr/jit/compiler.h | 388 +--- src/coreclr/jit/compiler.hpp | 202 -- src/coreclr/jit/fgbasic.cpp | 117 - src/coreclr/jit/fgdiagnostic.cpp | 510 +--- src/coreclr/jit/fgopt.cpp | 78 - src/coreclr/jit/flowgraph.cpp | 45 +- src/coreclr/jit/gentree.h | 1 - src/coreclr/jit/helperexpansion.cpp | 45 +- src/coreclr/jit/jitconfigvalues.h | 8 +- src/coreclr/jit/loopcloning.cpp | 10 +- src/coreclr/jit/loopcloning.h | 17 +- src/coreclr/jit/morph.cpp | 64 +- src/coreclr/jit/optimizebools.cpp | 12 +- src/coreclr/jit/optimizer.cpp | 2833 +++-------------------- src/coreclr/jit/phase.cpp | 2 +- src/coreclr/jit/redundantbranchopts.cpp | 36 - 19 files changed, 363 insertions(+), 4154 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 24d37e691c9ff8..01e137432ecf42 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -491,7 +491,7 @@ void BasicBlock::dspFlags() const {BBF_INTERNAL, "internal"}, {BBF_FAILED_VERIFICATION, "failV"}, {BBF_HAS_SUPPRESSGC_CALL, "sup-gc"}, - {BBF_LOOP_HEAD, "Loop"}, + {BBF_LOOP_HEAD, "loophead"}, {BBF_HAS_LABEL, "label"}, {BBF_HAS_JMP, "jmp"}, {BBF_HAS_CALL, "hascall"}, @@ -513,7 +513,7 @@ void BasicBlock::dspFlags() const {BBF_NO_CSE_IN, "no-cse"}, {BBF_CAN_ADD_PRED, "add-pred"}, {BBF_RETLESS_CALL, "retless"}, - {BBF_LOOP_PREHEADER, "LoopPH"}, + {BBF_LOOP_PREHEADER, "preheader"}, {BBF_COLD, "cold"}, {BBF_KEEP_BBJ_ALWAYS, "KEEP"}, {BBF_CLONED_FINALLY_BEGIN, "cfb"}, @@ -811,7 +811,6 @@ void BasicBlock::CloneBlockState( to->bbCodeOffs = from->bbCodeOffs; to->bbCodeOffsEnd = from->bbCodeOffsEnd; VarSetOps::AssignAllowUninitRhs(compiler, to->bbScope, from->bbScope); - to->bbNatLoopNum = from->bbNatLoopNum; #ifdef DEBUG to->bbTgtStkDepth = from->bbTgtStkDepth; #endif // DEBUG @@ -1606,11 +1605,6 @@ BasicBlock* BasicBlock::New(Compiler* compiler) block->bbMemorySsaNumOut[memoryKind] = 0; } - // Make sure we reserve a NOT_IN_LOOP value that isn't a legal table index. - static_assert_no_msg(BasicBlock::MAX_LOOP_NUM < BasicBlock::NOT_IN_LOOP); - - block->bbNatLoopNum = BasicBlock::NOT_IN_LOOP; - block->bbPreorderNum = 0; block->bbPostorderNum = 0; diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 2eef62974eed3a..0760baaab01659 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -43,7 +43,7 @@ typedef BitVec_ValRet_T ASSERT_VALRET_TP; // This define is used with string concatenation to put this in printf format strings (Note that %u means unsigned int) #define FMT_BB "BB%02u" -// Use this format for loop table indices. +// Use this format for loop indices #define FMT_LP "L%02u" // And this format for profile weights @@ -393,7 +393,7 @@ enum BasicBlockFlags : unsigned __int64 BBF_HAS_NULLCHECK = MAKE_BBFLAG(11), // BB contains a null check BBF_HAS_SUPPRESSGC_CALL = MAKE_BBFLAG(12), // BB contains a call to a method with SuppressGCTransitionAttribute BBF_RUN_RARELY = MAKE_BBFLAG(13), // BB is rarely run (catch clauses, blocks with throws etc) - BBF_LOOP_HEAD = MAKE_BBFLAG(14), // BB is the head of a loop + BBF_LOOP_HEAD = MAKE_BBFLAG(14), // BB is the head of a loop (can reach a predecessor) BBF_HAS_LABEL = MAKE_BBFLAG(15), // BB needs a label BBF_LOOP_ALIGN = MAKE_BBFLAG(16), // Block is lexically the first block in a loop we intend to align. BBF_HAS_ALIGN = MAKE_BBFLAG(17), // BB ends with 'align' instruction @@ -1251,14 +1251,6 @@ struct BasicBlock : private LIR::Range #define BBCT_FILTER_HANDLER 0xFFFFFFFF #define handlerGetsXcptnObj(hndTyp) ((hndTyp) != BBCT_NONE && (hndTyp) != BBCT_FAULT && (hndTyp) != BBCT_FINALLY) - // The following fields are used for loop detection - typedef unsigned char loopNumber; - static const unsigned NOT_IN_LOOP = UCHAR_MAX; - static const unsigned MAX_LOOP_NUM = 64; - - loopNumber bbNatLoopNum; // Index, in optLoopTable, of most-nested loop that contains this block, - // or else NOT_IN_LOOP if this block is not in a loop. - // TODO-Cleanup: Get rid of bbStkDepth and use bbStackDepthOnEntry() instead union { unsigned short bbStkDepth; // stack depth on entry diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 9e88c6ab091305..7fed233ed8cce8 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -305,7 +305,6 @@ Histogram computeReachabilitySetsIterationTable(computeReachabilitySetsIteration unsigned totalLoopMethods; // counts the total number of methods that have natural loops unsigned maxLoopsPerMethod; // counts the maximum number of loops a method has -unsigned totalLoopOverflows; // # of methods that identified more loops than we can represent unsigned totalLoopCount; // counts the total number of natural loops unsigned totalUnnatLoopCount; // counts the total number of (not-necessarily natural) loops unsigned totalUnnatLoopOverflows; // # of methods that identified more unnatural loops than we can represent @@ -1576,7 +1575,6 @@ void Compiler::compShutdown() jitprintf("Total number of methods with loops is %5u\n", totalLoopMethods); jitprintf("Total number of loops is %5u\n", totalLoopCount); jitprintf("Maximum number of loops per method is %5u\n", maxLoopsPerMethod); - jitprintf("# of methods overflowing nat loop table is %5u\n", totalLoopOverflows); jitprintf("Total number of 'unnatural' loops is %5u\n", totalUnnatLoopCount); jitprintf("# of methods overflowing unnat loop limit is %5u\n", totalUnnatLoopOverflows); jitprintf("Total number of loops with an iterator is %5u\n", iterLoopCount); @@ -5254,10 +5252,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // // Remarks: // All innermost loops whose block weight meets a threshold are candidates for alignment. -// The top block of the loop is marked with the BBF_LOOP_ALIGN flag to indicate this -// (the loop table itself is not changed). +// The top block of the loop is marked with the BBF_LOOP_ALIGN flag to indicate this. // -// Depends on the loop table, and on block weights being set. +// Depends on block weights being set. // bool Compiler::shouldAlignLoop(FlowGraphNaturalLoop* loop, BasicBlock* top) { @@ -5839,11 +5836,10 @@ void Compiler::RecomputeFlowGraphAnnotations() fgComputeReachability(); optSetBlockWeights(); - optFindLoops(); fgInvalidateDfsTree(); m_dfsTree = fgComputeDfs(); - optFindNewLoops(); + optFindLoops(); if (fgHasLoops) { @@ -5851,13 +5847,6 @@ void Compiler::RecomputeFlowGraphAnnotations() } m_domTree = FlowGraphDominatorTree::Build(m_dfsTree); - - // Dominators and the loop table are computed above for old<->new loop - // crossreferencing, but they are not actually used for optimization - // anymore. - optLoopTableValid = false; - optLoopTable = nullptr; - optLoopCount = 0; } /*****************************************************************************/ @@ -9084,7 +9073,7 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) fprintf(s_csvFile, "%u,", comp->info.compILCodeSize); fprintf(s_csvFile, "%u,", comp->fgBBcount); fprintf(s_csvFile, "%u,", comp->opts.MinOpts()); - fprintf(s_csvFile, "%u,", comp->optLoopCount); + fprintf(s_csvFile, "%u,", comp->optNumNaturalLoopsFound); fprintf(s_csvFile, "%u,", comp->optLoopsCloned); #if FEATURE_LOOP_ALIGN #ifdef DEBUG @@ -9337,16 +9326,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * cCVarSet, dCVarSet : Display a "converted" VARSET_TP: the varset is assumed to be tracked variable * indices. These are converted to variable numbers and sorted. (Calls * dumpConvertedVarSet()). - * cLoop, dLoop : Display the blocks of a loop, including the trees, given a loop number (call - * optPrintLoopInfo()). - * cLoopPtr, dLoopPtr : Display the blocks of a loop, including the trees, given a LoopDsc* (call - * optPrintLoopInfo()). - * cLoops, dLoops : Display the loop table (call optPrintLoopTable()). - * cNewLoops, dNewLoops : Display the loop table (call FlowGraphNaturalLoops::Dump()) with + * cLoops, dLoops : Display the loops (call FlowGraphNaturalLoops::Dump()) with * Compiler::m_loops. - * cNewLoopsA, dNewLoopsA : Display the loop table (call FlowGraphNaturalLoops::Dump()) with a given + * cLoopsA, dLoopsA : Display the loops (call FlowGraphNaturalLoops::Dump()) with a given * loops arg. - * cNewLoop, dNewLoop : Display a single loop (call FlowGraphNaturalLoop::Dump()) with given + * cLoop, dLoop : Display a single loop (call FlowGraphNaturalLoop::Dump()) with given * loop arg. * cTreeFlags, dTreeFlags : Display tree flags for a specified tree. * cVN, dVN : Display a ValueNum (call vnPrint()). @@ -9361,7 +9345,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * dFindTree : Find a tree in the IR, specifying a tree id. Sets `dbTree` and `dbTreeBlock`. * dFindStmt : Find a Statement in the IR, specifying a statement id. Sets `dbStmt`. * dFindBlock : Find a block in the IR, specifying a block number. Sets `dbBlock`. - * dFindLoop : Find a loop in the loop table, specifying a loop number. Sets `dbLoop`. + * dFindLoop : Find a loop specifying a loop index. Sets `dbLoop`. */ // Make the debug helpers available (under #ifdef DEBUG) even though they are unreferenced. When the Microsoft @@ -9401,12 +9385,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma comment(linker, "/include:cDoms") #pragma comment(linker, "/include:cLiveness") #pragma comment(linker, "/include:cCVarSet") -#pragma comment(linker, "/include:cLoop") -#pragma comment(linker, "/include:cLoopPtr") #pragma comment(linker, "/include:cLoops") -#pragma comment(linker, "/include:cNewLoops") -#pragma comment(linker, "/include:cNewLoopsA") -#pragma comment(linker, "/include:cNewLoop") +#pragma comment(linker, "/include:cLoopsA") +#pragma comment(linker, "/include:cLoop") #pragma comment(linker, "/include:cTreeFlags") #pragma comment(linker, "/include:cVN") @@ -9431,11 +9412,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma comment(linker, "/include:dLiveness") #pragma comment(linker, "/include:dCVarSet") #pragma comment(linker, "/include:dLoop") -#pragma comment(linker, "/include:dLoopPtr") #pragma comment(linker, "/include:dLoops") -#pragma comment(linker, "/include:dNewLoops") -#pragma comment(linker, "/include:dNewLoopsA") -#pragma comment(linker, "/include:dNewLoop") #pragma comment(linker, "/include:dTreeFlags") #pragma comment(linker, "/include:dVN") @@ -9473,11 +9450,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma comment(linker, "/include:_cLiveness") #pragma comment(linker, "/include:_cCVarSet") #pragma comment(linker, "/include:_cLoop") -#pragma comment(linker, "/include:_cLoopPtr") #pragma comment(linker, "/include:_cLoops") -#pragma comment(linker, "/include:_cNewLoops") -#pragma comment(linker, "/include:_cNewLoopsA") -#pragma comment(linker, "/include:_cNewLoop") +#pragma comment(linker, "/include:_cLoopsA") #pragma comment(linker, "/include:_cTreeFlags") #pragma comment(linker, "/include:_cVN") @@ -9501,12 +9475,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #pragma comment(linker, "/include:_dDoms") #pragma comment(linker, "/include:_dLiveness") #pragma comment(linker, "/include:_dCVarSet") -#pragma comment(linker, "/include:_dLoop") -#pragma comment(linker, "/include:_dLoopPtr") #pragma comment(linker, "/include:_dLoops") -#pragma comment(linker, "/include:_dNewLoops") -#pragma comment(linker, "/include:_dNewLoopsA") -#pragma comment(linker, "/include:_dNewLoop") +#pragma comment(linker, "/include:_dLoopsA") +#pragma comment(linker, "/include:_dLoop") #pragma comment(linker, "/include:_dTreeFlags") #pragma comment(linker, "/include:_dVN") @@ -9682,44 +9653,21 @@ JITDBGAPI void __cdecl cCVarSet(Compiler* comp, VARSET_VALARG_TP vars) printf("\n"); // dumpConvertedVarSet() doesn't emit a trailing newline } -JITDBGAPI void __cdecl cLoop(Compiler* comp, unsigned loopNum) -{ - static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called - printf("===================================================================== *Loop %u\n", sequenceNumber++); - comp->optPrintLoopInfo(loopNum, /* verbose */ true); - printf("\n"); -} - -JITDBGAPI void __cdecl cLoopPtr(Compiler* comp, const Compiler::LoopDsc* loop) -{ - static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called - printf("===================================================================== *LoopPtr %u\n", sequenceNumber++); - comp->optPrintLoopInfo(loop, /* verbose */ true); - printf("\n"); -} - JITDBGAPI void __cdecl cLoops(Compiler* comp) -{ - static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called - printf("===================================================================== *Loops %u\n", sequenceNumber++); - comp->optPrintLoopTable(); -} - -JITDBGAPI void __cdecl cNewLoops(Compiler* comp) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called printf("===================================================================== *NewLoops %u\n", sequenceNumber++); FlowGraphNaturalLoops::Dump(comp->m_loops); } -JITDBGAPI void __cdecl cNewLoopsA(Compiler* comp, FlowGraphNaturalLoops* loops) +JITDBGAPI void __cdecl cLoopsA(Compiler* comp, FlowGraphNaturalLoops* loops) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called printf("===================================================================== *NewLoopsA %u\n", sequenceNumber++); FlowGraphNaturalLoops::Dump(loops); } -JITDBGAPI void __cdecl cNewLoop(Compiler* comp, FlowGraphNaturalLoop* loop) +JITDBGAPI void __cdecl cLoop(Compiler* comp, FlowGraphNaturalLoop* loop) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called printf("===================================================================== *NewLoop %u\n", sequenceNumber++); @@ -9778,10 +9726,6 @@ JITDBGAPI void __cdecl cTreeFlags(Compiler* comp, GenTree* tree) { chars += printf("[VAR_USEASG]"); } - if (tree->gtFlags & GTF_VAR_ITERATOR) - { - chars += printf("[VAR_ITERATOR]"); - } if (tree->gtFlags & GTF_VAR_MOREUSES) { chars += printf("[VAR_MOREUSES]"); @@ -10318,34 +10262,19 @@ JITDBGAPI void __cdecl dCVarSet(VARSET_VALARG_TP vars) cCVarSet(JitTls::GetCompiler(), vars); } -JITDBGAPI void __cdecl dLoop(unsigned loopNum) -{ - cLoop(JitTls::GetCompiler(), loopNum); -} - -JITDBGAPI void __cdecl dLoopPtr(const Compiler::LoopDsc* loop) -{ - cLoopPtr(JitTls::GetCompiler(), loop); -} - JITDBGAPI void __cdecl dLoops() { cLoops(JitTls::GetCompiler()); } -JITDBGAPI void __cdecl dNewLoops() -{ - cNewLoops(JitTls::GetCompiler()); -} - -JITDBGAPI void __cdecl dNewLoopsA(FlowGraphNaturalLoops* loops) +JITDBGAPI void __cdecl dLoopsA(FlowGraphNaturalLoops* loops) { - cNewLoopsA(JitTls::GetCompiler(), loops); + cLoopsA(JitTls::GetCompiler(), loops); } -JITDBGAPI void __cdecl dNewLoop(FlowGraphNaturalLoop* loop) +JITDBGAPI void __cdecl dLoop(FlowGraphNaturalLoop* loop) { - cNewLoop(JitTls::GetCompiler(), loop); + cLoop(JitTls::GetCompiler(), loop); } JITDBGAPI void __cdecl dTreeFlags(GenTree* tree) @@ -10382,11 +10311,11 @@ JITDBGAPI void __cdecl dBlockList(BasicBlockList* list) // Trees, Stmts, and/or Blocks using id or bbNum. // That can be used in watch window or as a way to get address of fields for data breakpoints. -GenTree* dbTree; -Statement* dbStmt; -BasicBlock* dbTreeBlock; -BasicBlock* dbBlock; -Compiler::LoopDsc* dbLoop; +GenTree* dbTree; +Statement* dbStmt; +BasicBlock* dbTreeBlock; +BasicBlock* dbBlock; +FlowGraphNaturalLoop* dbLoop; // Debug APIs for finding Trees, Stmts, and/or Blocks. // As a side effect, they set the debug variables above. @@ -10478,18 +10407,18 @@ JITDBGAPI BasicBlock* __cdecl dFindBlock(unsigned bbNum) return nullptr; } -JITDBGAPI Compiler::LoopDsc* __cdecl dFindLoop(unsigned loopNum) +JITDBGAPI FlowGraphNaturalLoop* __cdecl dFindLoop(unsigned index) { Compiler* comp = JitTls::GetCompiler(); dbLoop = nullptr; - if (loopNum >= comp->optLoopCount) + if ((comp->m_loops == nullptr) || (index >= comp->m_loops->NumLoops())) { - printf("loopNum %u out of range\n", loopNum); + printf("Index %u out of range\n", index); return nullptr; } - dbLoop = &comp->optLoopTable[loopNum]; + dbLoop = comp->m_loops->GetLoopByIndex(index); return dbLoop; } diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5bf68dfff8bbdb..0f979e47009383 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1516,7 +1516,7 @@ enum class PhaseChecks : unsigned int CHECK_UNIQUE = 1 << 1, // tree node uniqueness CHECK_FG = 1 << 2, // flow graph integrity CHECK_EH = 1 << 3, // eh table integrity - CHECK_LOOPS = 1 << 4, // loop table integrity + CHECK_LOOPS = 1 << 4, // loop integrity/canonicalization CHECK_PROFILE = 1 << 5, // profile data integrity CHECK_LINKED_LOCALS = 1 << 6, // check linked list of locals }; @@ -1887,60 +1887,6 @@ typedef JitHashTable, TestLabelAndNum> NodeToT // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif // DEBUG -//------------------------------------------------------------------------- -// LoopFlags: flags for the loop table. -// -enum LoopFlags : unsigned short -{ - LPFLG_EMPTY = 0, - - // LPFLG_UNUSED = 0x0001, - // LPFLG_UNUSED = 0x0002, - LPFLG_ITER = 0x0004, // loop of form: for (i = icon or expression; test_condition(); i++) - // LPFLG_UNUSED = 0x0008, - - LPFLG_CONTAINS_CALL = 0x0010, // If executing the loop body *may* execute a call - // LPFLG_UNUSED = 0x0020, - LPFLG_CONST_INIT = 0x0040, // iterator is initialized with a constant (found in lpConstInit) - LPFLG_SIMD_LIMIT = 0x0080, // iterator is compared with vector element count (found in lpConstLimit) - - LPFLG_VAR_LIMIT = 0x0100, // iterator is compared with a local var (var # found in lpVarLimit) - LPFLG_CONST_LIMIT = 0x0200, // iterator is compared with a constant (found in lpConstLimit) - LPFLG_ARRLEN_LIMIT = 0x0400, // iterator is compared with a.len or a[i].len (found in lpArrLenLimit) - LPFLG_HAS_PREHEAD = 0x0800, // lpHead is known to be a preHead for this loop - - LPFLG_REMOVED = 0x1000, // has been removed from the loop table (unrolled or optimized away) - LPFLG_DONT_UNROLL = 0x2000, // do not unroll this loop - LPFLG_ASGVARS_YES = 0x4000, // "lpAsgVars" has been computed - LPFLG_ASGVARS_INC = 0x8000, // "lpAsgVars" is incomplete -- vars beyond those representable in an AllVarSet - // type are assigned to. -}; - -inline constexpr LoopFlags operator~(LoopFlags a) -{ - return (LoopFlags)(~(unsigned short)a); -} - -inline constexpr LoopFlags operator|(LoopFlags a, LoopFlags b) -{ - return (LoopFlags)((unsigned short)a | (unsigned short)b); -} - -inline constexpr LoopFlags operator&(LoopFlags a, LoopFlags b) -{ - return (LoopFlags)((unsigned short)a & (unsigned short)b); -} - -inline LoopFlags& operator|=(LoopFlags& a, LoopFlags b) -{ - return a = (LoopFlags)((unsigned short)a | (unsigned short)b); -} - -inline LoopFlags& operator&=(LoopFlags& a, LoopFlags b) -{ - return a = (LoopFlags)((unsigned short)a & (unsigned short)b); -} - // Represents a depth-first search tree of the flow graph. class FlowGraphDfsTree { @@ -5014,6 +4960,9 @@ class Compiler FlowGraphDominatorTree* m_domTree; BlockReachabilitySets* m_reachabilitySets; + bool optLoopsRequirePreHeaders; // Do we require that all loops (in m_loops) have pre-headers? + unsigned optNumNaturalLoopsFound; // Number of natural loops found in the loop finding phase + bool fgBBVarSetsInited; // Track how many artificial ref counts we've added to fgEntryBB (for OSR) @@ -5970,8 +5919,6 @@ class Compiler void fgCompactBlocks(BasicBlock* block, BasicBlock* bNext); - void fgUpdateLoopsAfterCompacting(BasicBlock* block, BasicBlock* bNext); - BasicBlock* fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst, bool noFallThroughQuirk = false); bool fgRenumberBlocks(); @@ -6107,7 +6054,7 @@ class Compiler void fgDebugCheckNodeLinks(BasicBlock* block, Statement* stmt); void fgDebugCheckLinkedLocals(); void fgDebugCheckNodesUniqueness(); - void fgDebugCheckLoopTable(); + void fgDebugCheckLoops(); void fgDebugCheckSsa(); void fgDebugCheckTypes(GenTree* tree); @@ -6798,13 +6745,6 @@ class Compiler // Compute the sets of long and float vars (lvaLongVars, lvaFloatVars). void optComputeInterestingVarSets(); -#ifdef DEBUG - bool optAnyChildNotRemoved(unsigned loopNum); -#endif // DEBUG - - // Mark a loop as removed. - void optMarkLoopRemoved(unsigned loopNum); - private: // Given a loop mark it and any nested loops as having 'memoryHavoc' void optRecordLoopNestsMemoryHavoc(FlowGraphNaturalLoop* loop, MemoryKindSet memoryHavoc); @@ -6828,7 +6768,6 @@ class Compiler PhaseStatus optFindLoopsPhase(); // Finds loops and records them in the loop table void optFindLoops(); - void optFindNewLoops(); bool optCanonicalizeLoops(); bool optCompactLoops(); bool optCompactLoop(FlowGraphNaturalLoop* loop); @@ -6845,268 +6784,13 @@ class Compiler void optRemoveRedundantZeroInits(); PhaseStatus optIfConversion(); // If conversion -protected: - // This enumeration describes what is killed by a call. - - enum callInterf - { - CALLINT_NONE, // no interference (most helpers) - CALLINT_REF_INDIRS, // kills GC ref indirections (SETFIELD OBJ) - CALLINT_SCL_INDIRS, // kills non GC ref indirections (SETFIELD non-OBJ) - CALLINT_ALL_INDIRS, // kills both GC ref and non GC ref indirections (SETFIELD STRUCT) - CALLINT_ALL, // kills everything (normal method call) - }; - -public: - // A "LoopDsc" describes a ("natural") loop. We (currently) require the body of a loop to be a contiguous (in - // bbNext order) sequence of basic blocks. (At times, we may require the blocks in a loop to be "properly numbered" - // in bbNext order; we use comparisons on the bbNum to decide order.) - // The blocks that define the body are - // top <= entry <= bottom - // The "head" of the loop is a block outside the loop that has "entry" as a successor. We only support loops with a - // single 'head' block. The meanings of these blocks are given in the definitions below. Also see the picture at - // Compiler::optFindNaturalLoops(). - struct LoopDsc - { - BasicBlock* lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor. - BasicBlock* lpTop; // loop TOP (the back edge from lpBottom reaches here). Lexically first block (in bbNext - // order) reachable in this loop. - BasicBlock* lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM) - BasicBlock* lpBottom; // loop BOTTOM (from here we have a back edge to the TOP) - BasicBlock* lpExit; // if a single exit loop this is the EXIT (in most cases BOTTOM) - - callInterf lpAsgCall; // "callInterf" for calls in the loop - ALLVARSET_TP lpAsgVars; // set of vars assigned within the loop (all vars, not just tracked) - varRefKinds lpAsgInds : 8; // set of inds modified within the loop - - LoopFlags lpFlags; - - unsigned char lpExitCnt; // number of exits from the loop - - unsigned char lpParent; // The index of the most-nested loop that completely contains this one, - // or else BasicBlock::NOT_IN_LOOP if no such loop exists. - unsigned char lpChild; // The index of a nested loop, or else BasicBlock::NOT_IN_LOOP if no child exists. - // (Actually, an "immediately" nested loop -- - // no other child of this loop is a parent of lpChild.) - unsigned char lpSibling; // The index of another loop that is an immediate child of lpParent, - // or else BasicBlock::NOT_IN_LOOP. One can enumerate all the children of a loop - // by following "lpChild" then "lpSibling" links. - - bool lpLoopHasMemoryHavoc[MemoryKindCount]; // The loop contains an operation that we assume has arbitrary - // memory side effects. If this is set, the fields below - // may not be accurate (since they become irrelevant.) - - VARSET_TP lpVarInOut; // The set of variables that are IN or OUT during the execution of this loop - VARSET_TP lpVarUseDef; // The set of variables that are USE or DEF during the execution of this loop - - // The following counts are used for hoisting profitability checks. - - int lpHoistedExprCount; // The register count for the non-FP expressions from inside this loop that have been - // hoisted - int lpLoopVarCount; // The register count for the non-FP LclVars that are read/written inside this loop - int lpVarInOutCount; // The register count for the non-FP LclVars that are alive inside or across this loop - - int lpHoistedFPExprCount; // The register count for the FP expressions from inside this loop that have been - // hoisted - int lpLoopVarFPCount; // The register count for the FP LclVars that are read/written inside this loop - int lpVarInOutFPCount; // The register count for the FP LclVars that are alive inside or across this loop - - typedef JitHashTable, FieldKindForVN> - FieldHandleSet; - FieldHandleSet* lpFieldsModified; // This has entries for all static field and object instance fields modified - // in the loop. - - typedef JitHashTable, bool> ClassHandleSet; - ClassHandleSet* lpArrayElemTypesModified; // Bits set indicate the set of sz array element types such that - // arrays of that type are modified - // in the loop. - - /* The following values are set only for iterator loops, i.e. has the flag LPFLG_ITER set */ - - GenTree* lpIterTree; // The "i = i const" tree - unsigned lpIterVar() const; // iterator variable # - int lpIterConst() const; // the constant with which the iterator is incremented - genTreeOps lpIterOper() const; // the type of the operation on the iterator (ADD, SUB, etc.) - void VERIFY_lpIterTree() const; - - var_types lpIterOperType() const; // For overflow instructions - - // Set to the block where we found the initialization for LPFLG_CONST_INIT loops. - // Initially, this will be 'head', but 'head' might change if we insert a loop pre-header block. - BasicBlock* lpInitBlock; - - int lpConstInit; // initial constant value of iterator : Valid if LPFLG_CONST_INIT - - // The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var") - - GenTree* lpTestTree; // pointer to the node containing the loop test - genTreeOps lpTestOper() const; // the type of the comparison between the iterator and the limit (GT_LE, GT_GE, - // etc.) - - bool lpIsIncreasingLoop() const; // if the loop iterator increases from low to high value. - bool lpIsDecreasingLoop() const; // if the loop iterator decreases from high to low value. - - void VERIFY_lpTestTree() const; - - bool lpIsReversed() const; // true if the iterator node is the second operand in the loop condition - GenTree* lpIterator() const; // the iterator node in the loop test - GenTree* lpLimit() const; // the limit node in the loop test - - // Limit constant value of iterator - loop condition is "i RELOP const" - // : Valid if LPFLG_CONST_LIMIT - int lpConstLimit() const; - - // The lclVar # in the loop condition ( "i RELOP lclVar" ) - // : Valid if LPFLG_VAR_LIMIT - unsigned lpVarLimit() const; - - // The array length in the loop condition ( "i RELOP arr.len" or "i RELOP arr[i][j].len" ) - // : Valid if LPFLG_ARRLEN_LIMIT - bool lpArrLenLimit(Compiler* comp, ArrIndex* index) const; - - // Returns "true" iff this is a "top entry" loop. - bool lpIsTopEntry() const - { - if (lpHead->NextIs(lpEntry)) - { - assert(lpHead->bbFallsThrough() || lpHead->JumpsToNext()); - assert(lpTop == lpEntry); - return true; - } - else - { - return false; - } - } - - // Returns "true" iff this is removed loop. - bool lpIsRemoved() const - { - return (lpFlags & LPFLG_REMOVED) != 0; - } - - // Returns "true" iff "*this" contains the blk. - bool lpContains(BasicBlock* blk) const - { - return lpTop->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum; - } - - // Returns "true" iff "*this" (properly) contains the range [top, bottom] (allowing tops - // to be equal, but requiring bottoms to be different.) - bool lpContains(BasicBlock* top, BasicBlock* bottom) const - { - return lpTop->bbNum <= top->bbNum && bottom->bbNum < lpBottom->bbNum; - } - - // Returns "true" iff "*this" (properly) contains "lp2" (allowing tops to be equal, but requiring - // bottoms to be different.) - bool lpContains(const LoopDsc& lp2) const - { - return lpContains(lp2.lpTop, lp2.lpBottom); - } - - // Returns "true" iff "*this" is (properly) contained by the range [top, bottom] - // (allowing tops to be equal, but requiring bottoms to be different.) - bool lpContainedBy(BasicBlock* top, BasicBlock* bottom) const - { - return top->bbNum <= lpTop->bbNum && lpBottom->bbNum < bottom->bbNum; - } - - // Returns "true" iff "*this" is (properly) contained by "lp2" - // (allowing tops to be equal, but requiring bottoms to be different.) - bool lpContainedBy(const LoopDsc& lp2) const - { - return lpContainedBy(lp2.lpTop, lp2.lpBottom); - } - - // Returns "true" iff "*this" is disjoint from the range [top, bottom]. - bool lpDisjoint(BasicBlock* top, BasicBlock* bottom) const - { - return bottom->bbNum < lpTop->bbNum || lpBottom->bbNum < top->bbNum; - } - // Returns "true" iff "*this" is disjoint from "lp2". - bool lpDisjoint(const LoopDsc& lp2) const - { - return lpDisjoint(lp2.lpTop, lp2.lpBottom); - } - - // Returns "true" iff the loop is well-formed (see code for defn). - bool lpWellFormed() const - { - return lpTop->bbNum <= lpEntry->bbNum && lpEntry->bbNum <= lpBottom->bbNum && - (lpHead->bbNum < lpTop->bbNum || lpHead->bbNum > lpBottom->bbNum); - } - -#ifdef DEBUG - void lpValidatePreHeader() const - { - // If this is called, we expect there to be a pre-header. - assert(lpFlags & LPFLG_HAS_PREHEAD); - - // The pre-header must unconditionally enter the loop. - assert(lpHead->GetUniqueSucc() == lpEntry); - - // The loop block must be marked as a pre-header. - assert(lpHead->HasFlag(BBF_LOOP_PREHEADER)); - - // The loop entry must have a single non-loop predecessor, which is the pre-header. - // We can't assume here that the bbNum are properly ordered, so we can't do a simple lpContained() - // check. So, we defer this check, which will be done by `fgDebugCheckLoopTable()`. - } -#endif // DEBUG - - // LoopBlocks: convenience method for enabling range-based `for` iteration over all the - // blocks in a loop, e.g.: - // for (BasicBlock* const block : loop->LoopBlocks()) ... - // Currently, the loop blocks are expected to be in linear, lexical, `bbNext` order - // from `lpTop` through `lpBottom`, inclusive. All blocks in this range are considered - // to be part of the loop. - // - BasicBlockRangeList LoopBlocks() const - { - return BasicBlockRangeList(lpTop, lpBottom); - } - }; - -protected: - bool fgMightHaveLoop(); // returns true if there are any back edges - bool fgHasLoops; // True if this method has any loops, set in fgComputeReachability - public: - LoopDsc* optLoopTable; // loop descriptor table - bool optLoopTableValid; // info in loop table should be valid - bool optLoopsRequirePreHeaders; // Do we require that all loops (in the loop table) have pre-headers? - unsigned char optLoopCount; // number of tracked loops - - // Every time we rebuild the loop table, we increase the global "loop epoch". Any loop indices or - // loop table pointers from the previous epoch are invalid. - // TODO: validate this in some way? - unsigned optCurLoopEpoch; - - void NewLoopEpoch() - { - ++optCurLoopEpoch; - JITDUMP("New loop epoch %d\n", optCurLoopEpoch); - } - + bool fgHasLoops; #ifdef DEBUG unsigned loopAlignCandidates; // number of candidates identified by placeLoopAlignInstructions unsigned loopsAligned; // number of loops actually aligned #endif // DEBUG - bool optRecordLoop(BasicBlock* head, - BasicBlock* top, - BasicBlock* entry, - BasicBlock* bottom, - BasicBlock* exit, - unsigned char exitCnt); - -#ifdef DEBUG - void optPrintLoopInfo(unsigned lnum, bool printVerbose = false); - void optPrintLoopInfo(const LoopDsc* loop, bool printVerbose = false); - void optPrintLoopTable(); -#endif - protected: unsigned optCallCount; // number of calls made in the method unsigned optIndirectCallCount; // number of virtual, interface and indirect calls made in the method @@ -7125,34 +6809,11 @@ class Compiler void optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk); - void optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk); - - void optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmarkLoop = false); - bool optIsLoopTestEvalIntoTemp(Statement* testStmt, Statement** newTestStmt); unsigned optIsLoopIncrTree(GenTree* incr); - bool optCheckIterInLoopTest(unsigned loopInd, GenTree* test, unsigned iterVar); - bool optComputeIterInfo(GenTree* incr, BasicBlock* from, BasicBlock* to, unsigned* pIterVar); - bool optPopulateInitInfo(unsigned loopInd, BasicBlock* initBlock, GenTree* init, unsigned iterVar); bool optExtractInitTestIncr( BasicBlock** pInitBlock, BasicBlock* bottom, BasicBlock* top, GenTree** ppInit, GenTree** ppTest, GenTree** ppIncr); - void optFindNaturalLoops(); - - // Requires "l1" to be a valid loop table index, and not "BasicBlock::NOT_IN_LOOP". - // Requires "l2" to be a valid loop table index, or else "BasicBlock::NOT_IN_LOOP". - // Returns true iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2". - // A loop contains itself. - bool optLoopContains(unsigned l1, unsigned l2) const; - - // Returns the lpEntry for given preheader block of a loop - BasicBlock* optLoopEntry(BasicBlock* preHeader); - - // Updates the loop table by changing loop "loopInd", whose head is required - // to be "from", to be "to". Also performs this transformation for any - // loop nested in "loopInd" that shares the same head as "loopInd". - void optUpdateLoopHead(unsigned loopInd, BasicBlock* from, BasicBlock* to); - enum class RedirectBlockOption { DoNotChangePredLists, // do not modify pred lists @@ -7176,21 +6837,6 @@ class Compiler // Adds "elemType" to the set of modified array element types of "loop" and any parent loops. void AddModifiedElemTypeAllContainingLoops(FlowGraphNaturalLoop* loop, CORINFO_CLASS_HANDLE elemType); - // Returns true if 'block' is an entry block for any loop in 'optLoopTable' - bool optIsLoopEntry(BasicBlock* block) const; - - // The depth of the loop described by "lnum" (an index into the loop table.) (0 == top level) - unsigned optLoopDepth(unsigned lnum) const - { - assert(lnum < optLoopCount); - unsigned depth = 0; - while ((lnum = optLoopTable[lnum].lpParent) != BasicBlock::NOT_IN_LOOP) - { - ++depth; - } - return depth; - } - // Struct used in optInvertWhileLoop to count interesting constructs to boost the profitability score. struct OptInvertCountTreeInfoType { @@ -7217,27 +6863,8 @@ class Compiler unsigned* iterCount); protected: - struct isVarAssgDsc - { - GenTree* ivaSkip; - ALLVARSET_TP ivaMaskVal; // Set of variables assigned to. This is a set of all vars, not tracked vars. - unsigned ivaVar; // Variable we are interested in, or -1 - varRefKinds ivaMaskInd; // What kind of indirect assignments are there? - callInterf ivaMaskCall; // What kind of calls are there? - bool ivaMaskIncomplete; // Variables not representable in ivaMaskVal were assigned to. - }; - - bool optIsVarAssignedWithDesc(Statement* stmt, isVarAssgDsc* dsc); - - bool optIsVarAssigned(BasicBlock* beg, BasicBlock* end, GenTree* skip, unsigned var); - - bool optIsVarAssgLoop(unsigned lnum, unsigned var); - - bool optIsSetAssgLoop(unsigned lnum, ALLVARSET_VALARG_TP vars, varRefKinds inds = VR_NONE); - bool optNarrowTree(GenTree* tree, var_types srct, var_types dstt, ValueNumPair vnpNarrow, bool doit); -protected: // The following is the upper limit on how many expressions we'll keep track // of for the CSE analysis. // @@ -7423,8 +7050,6 @@ class Compiler void optOptimizeCSEs(); - static callInterf optCallInterf(GenTreeCall* call); - public: // VN based copy propagation. @@ -12217,7 +11842,6 @@ extern Histogram computeReachabilitySetsIterationTable; extern unsigned totalLoopMethods; // counts the total number of methods that have natural loops extern unsigned maxLoopsPerMethod; // counts the maximum number of loops a method has -extern unsigned totalLoopOverflows; // # of methods that identified more loops than we can represent extern unsigned totalLoopCount; // counts the total number of natural loops extern unsigned totalUnnatLoopCount; // counts the total number of (not-necessarily natural) loops extern unsigned totalUnnatLoopOverflows; // # of methods that identified more unnatural loops than we can represent diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index b00f3e66139216..a4cc4bcd13c089 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3616,208 +3616,6 @@ inline void Compiler::optAssertionRemove(AssertionIndex index) } } -inline void Compiler::LoopDsc::VERIFY_lpIterTree() const -{ -#ifdef DEBUG - assert(lpFlags & LPFLG_ITER); - - // iterTree should be "lcl = lcl const" - - assert(lpIterTree->OperIs(GT_STORE_LCL_VAR)); - - const GenTree* value = lpIterTree->AsLclVar()->Data(); - assert(value->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_RSH, GT_LSH)); - assert(value->AsOp()->gtOp1->OperGet() == GT_LCL_VAR); - assert(value->AsOp()->gtOp1->AsLclVar()->GetLclNum() == lpIterTree->AsLclVar()->GetLclNum()); - assert(value->AsOp()->gtOp2->OperGet() == GT_CNS_INT); -#endif -} - -//----------------------------------------------------------------------------- - -inline unsigned Compiler::LoopDsc::lpIterVar() const -{ - VERIFY_lpIterTree(); - return lpIterTree->AsLclVar()->GetLclNum(); -} - -//----------------------------------------------------------------------------- - -inline int Compiler::LoopDsc::lpIterConst() const -{ - VERIFY_lpIterTree(); - GenTree* value = lpIterTree->AsLclVar()->Data(); - return (int)value->AsOp()->gtOp2->AsIntCon()->gtIconVal; -} - -//----------------------------------------------------------------------------- - -inline genTreeOps Compiler::LoopDsc::lpIterOper() const -{ - VERIFY_lpIterTree(); - return lpIterTree->AsLclVar()->Data()->OperGet(); -} - -inline var_types Compiler::LoopDsc::lpIterOperType() const -{ - VERIFY_lpIterTree(); - - var_types type = lpIterTree->TypeGet(); - assert(genActualType(type) == TYP_INT); - - return type; -} - -inline void Compiler::LoopDsc::VERIFY_lpTestTree() const -{ -#ifdef DEBUG - assert(lpFlags & LPFLG_ITER); - assert(lpTestTree); - - genTreeOps oper = lpTestTree->OperGet(); - assert(GenTree::OperIsCompare(oper)); - - GenTree* iterator = nullptr; - GenTree* limit = nullptr; - if ((lpTestTree->AsOp()->gtOp2->gtOper == GT_LCL_VAR) && - (lpTestTree->AsOp()->gtOp2->gtFlags & GTF_VAR_ITERATOR) != 0) - { - iterator = lpTestTree->AsOp()->gtOp2; - limit = lpTestTree->AsOp()->gtOp1; - } - else if ((lpTestTree->AsOp()->gtOp1->gtOper == GT_LCL_VAR) && - (lpTestTree->AsOp()->gtOp1->gtFlags & GTF_VAR_ITERATOR) != 0) - { - iterator = lpTestTree->AsOp()->gtOp1; - limit = lpTestTree->AsOp()->gtOp2; - } - else - { - // one of the nodes has to be the iterator - assert(false); - } - - if (lpFlags & LPFLG_CONST_LIMIT) - { - assert(limit->OperIsConst()); - } - if (lpFlags & LPFLG_VAR_LIMIT) - { - assert(limit->OperGet() == GT_LCL_VAR); - } - if (lpFlags & LPFLG_ARRLEN_LIMIT) - { - assert(limit->OperGet() == GT_ARR_LENGTH); - } -#endif -} - -//----------------------------------------------------------------------------- - -inline bool Compiler::LoopDsc::lpIsReversed() const -{ - VERIFY_lpTestTree(); - return ((lpTestTree->AsOp()->gtOp2->gtOper == GT_LCL_VAR) && - (lpTestTree->AsOp()->gtOp2->gtFlags & GTF_VAR_ITERATOR) != 0); -} - -//----------------------------------------------------------------------------- - -inline genTreeOps Compiler::LoopDsc::lpTestOper() const -{ - VERIFY_lpTestTree(); - genTreeOps op = lpTestTree->OperGet(); - return lpIsReversed() ? GenTree::SwapRelop(op) : op; -} - -//----------------------------------------------------------------------------- - -inline bool Compiler::LoopDsc::lpIsIncreasingLoop() const -{ - // Increasing loop is the one that has "+=" increment operation and "< or <=" limit check. - bool isLessThanLimitCheck = GenTree::StaticOperIs(lpTestOper(), GT_LT, GT_LE); - return (isLessThanLimitCheck && - (((lpIterOper() == GT_ADD) && (lpIterConst() > 0)) || ((lpIterOper() == GT_SUB) && (lpIterConst() < 0)))); -} - -//----------------------------------------------------------------------------- - -inline bool Compiler::LoopDsc::lpIsDecreasingLoop() const -{ - // Decreasing loop is the one that has "-=" decrement operation and "> or >=" limit check. If the operation is - // "+=", make sure the constant is negative to give an effect of decrementing the iterator. - bool isGreaterThanLimitCheck = GenTree::StaticOperIs(lpTestOper(), GT_GT, GT_GE); - return (isGreaterThanLimitCheck && - (((lpIterOper() == GT_ADD) && (lpIterConst() < 0)) || ((lpIterOper() == GT_SUB) && (lpIterConst() > 0)))); -} - -//----------------------------------------------------------------------------- - -inline GenTree* Compiler::LoopDsc::lpIterator() const -{ - VERIFY_lpTestTree(); - - return lpIsReversed() ? lpTestTree->AsOp()->gtOp2 : lpTestTree->AsOp()->gtOp1; -} - -//----------------------------------------------------------------------------- - -inline GenTree* Compiler::LoopDsc::lpLimit() const -{ - VERIFY_lpTestTree(); - - return lpIsReversed() ? lpTestTree->AsOp()->gtOp1 : lpTestTree->AsOp()->gtOp2; -} - -//----------------------------------------------------------------------------- - -inline int Compiler::LoopDsc::lpConstLimit() const -{ - VERIFY_lpTestTree(); - assert(lpFlags & LPFLG_CONST_LIMIT); - - GenTree* limit = lpLimit(); - assert(limit->OperIsConst()); - return (int)limit->AsIntCon()->gtIconVal; -} - -//----------------------------------------------------------------------------- - -inline unsigned Compiler::LoopDsc::lpVarLimit() const -{ - VERIFY_lpTestTree(); - assert(lpFlags & LPFLG_VAR_LIMIT); - - GenTree* limit = lpLimit(); - assert(limit->OperGet() == GT_LCL_VAR); - return limit->AsLclVarCommon()->GetLclNum(); -} - -//----------------------------------------------------------------------------- - -inline bool Compiler::LoopDsc::lpArrLenLimit(Compiler* comp, ArrIndex* index) const -{ - VERIFY_lpTestTree(); - assert(lpFlags & LPFLG_ARRLEN_LIMIT); - - GenTree* limit = lpLimit(); - assert(limit->OperGet() == GT_ARR_LENGTH); - - // Check if we have a.length or a[i][j].length - if (limit->AsArrLen()->ArrRef()->gtOper == GT_LCL_VAR) - { - index->arrLcl = limit->AsArrLen()->ArrRef()->AsLclVarCommon()->GetLclNum(); - index->rank = 0; - return true; - } - // We have a[i].length, extract a[i] pattern. - else if (limit->AsArrLen()->ArrRef()->gtOper == GT_COMMA) - { - return comp->optReconstructArrIndex(limit->AsArrLen()->ArrRef(), index); - } - return false; -} - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 6acdd220f20b0c..3b768ab43e7d1b 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -4924,17 +4924,6 @@ BasicBlock* Compiler::fgSplitBlockBeforeTree( assert(prevBb->KindIs(BBJ_ALWAYS) && prevBb->JumpsToNext() && prevBb->NextIs(block)); prevBb->SetFlags(BBF_NONE_QUIRK); - if (optLoopTableValid && prevBb->bbNatLoopNum != BasicBlock::NOT_IN_LOOP) - { - block->bbNatLoopNum = prevBb->bbNatLoopNum; - - // Update lpBottom after block split - if (optLoopTable[prevBb->bbNatLoopNum].lpBottom == prevBb) - { - optLoopTable[prevBb->bbNatLoopNum].lpBottom = block; - } - } - return block; } @@ -5355,9 +5344,6 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) } #endif // FEATURE_EH_FUNCLETS - /* First update the loop table and bbWeights */ - optUpdateLoopsBeforeRemoveBlock(block, skipUnmarkLoop); - // Update successor block start IL offset, if empty predecessor // covers the immediately preceding range. if ((block->bbCodeOffsEnd == succBlock->bbCodeOffs) && (block->bbCodeOffs != BAD_IL_OFFSET)) @@ -5390,14 +5376,6 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) { BasicBlock* predBlock = pred->getSourceBlock(); - /* Are we changing a loop backedge into a forward jump? */ - - if (block->isLoopHead() && (predBlock->bbNum >= block->bbNum) && (predBlock->bbNum <= succBlock->bbNum)) - { - /* First update the loop table and bbWeights */ - optUpdateLoopsBeforeRemoveBlock(predBlock); - } - /* If predBlock is a new predecessor, then add it to succBlock's predecessor's list. */ if (!predBlock->KindIs(BBJ_SWITCH)) @@ -5556,9 +5534,6 @@ BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst, b bSrc->SetFalseTarget(jmpBlk); fgAddRefPred(jmpBlk, bSrc, fgGetPredForBlock(bDst, bSrc)); - // Record the loop number in the new block - jmpBlk->bbNatLoopNum = bSrc->bbNatLoopNum; - // When adding a new jmpBlk we will set the bbWeight and bbFlags // if (fgHaveValidEdgeWeights && fgHaveProfileWeights()) @@ -6161,46 +6136,6 @@ BasicBlock* Compiler::fgRelocateEHRange(unsigned regionIndex, FG_RELOCATE_TYPE r return bLast; } -//------------------------------------------------------------------------ -// fgMightHaveLoop: return true if there is a possibility that the method has a loop (a back edge is present). -// This function doesn't depend on any previous loop computations, including predecessors. It looks for any -// lexical back edge to a block previously seen in a forward walk of the block list. -// -// As it walks all blocks and all successors of each block (including EH successors), it is not cheap. -// It returns as soon as any possible loop is discovered. -// -// Return Value: -// true if there might be a loop -// -bool Compiler::fgMightHaveLoop() -{ - // Don't use a BlockSet for this temporary bitset of blocks: we don't want to have to call EnsureBasicBlockEpoch() - // and potentially change the block epoch. - - BitVecTraits blockVecTraits(fgBBNumMax + 1, this); - BitVec blocksSeen(BitVecOps::MakeEmpty(&blockVecTraits)); - - for (BasicBlock* const block : Blocks()) - { - BitVecOps::AddElemD(&blockVecTraits, blocksSeen, block->bbNum); - - BasicBlockVisit result = block->VisitAllSuccs(this, [&](BasicBlock* succ) { - if (BitVecOps::IsMember(&blockVecTraits, blocksSeen, succ->bbNum)) - { - return BasicBlockVisit::Abort; - } - - return BasicBlockVisit::Continue; - }); - - if (result == BasicBlockVisit::Abort) - { - return true; - } - } - return false; -} - /***************************************************************************** * * Insert a BasicBlock before the given block. @@ -7072,58 +7007,6 @@ BasicBlock* Compiler::fgNewBBinRegionWorker(BBKinds jumpKind, /* If afterBlk falls through, we insert a jump around newBlk */ fgConnectFallThrough(afterBlk, newBlk->Next()); - // If the loop table is valid, add this block to the appropriate loop. - // Note we don't verify (via flow) that this block actually belongs - // to the loop, just that it is lexically within the span of blocks - // in the loop. - // - if (optLoopTableValid) - { - BasicBlock* const bbPrev = newBlk->Prev(); - BasicBlock* const bbNext = newBlk->Next(); - - if ((bbPrev != nullptr) && (bbNext != nullptr)) - { - BasicBlock::loopNumber const prevLoopNum = bbPrev->bbNatLoopNum; - BasicBlock::loopNumber const nextLoopNum = bbNext->bbNatLoopNum; - - if ((prevLoopNum != BasicBlock::NOT_IN_LOOP) && (nextLoopNum != BasicBlock::NOT_IN_LOOP)) - { - if (prevLoopNum == nextLoopNum) - { - newBlk->bbNatLoopNum = prevLoopNum; - } - else - { - BasicBlock::loopNumber const prevParentLoopNum = optLoopTable[prevLoopNum].lpParent; - BasicBlock::loopNumber const nextParentLoopNum = optLoopTable[nextLoopNum].lpParent; - - if (nextParentLoopNum == prevLoopNum) - { - // next is in child loop - newBlk->bbNatLoopNum = prevLoopNum; - } - else if (prevParentLoopNum == nextLoopNum) - { - // prev is in child loop - newBlk->bbNatLoopNum = nextLoopNum; - } - else - { - // next and prev are siblings - assert(prevParentLoopNum == nextParentLoopNum); - newBlk->bbNatLoopNum = prevParentLoopNum; - } - } - } - } - - if (newBlk->bbNatLoopNum != BasicBlock::NOT_IN_LOOP) - { - JITDUMP("Marked " FMT_BB " as lying within " FMT_LP "\n", newBlk->bbNum, newBlk->bbNatLoopNum); - } - } - #ifdef DEBUG fgVerifyHandlerTab(); #endif diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index c0f1ade1d077e4..f1f1afd357bced 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -711,24 +711,19 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) bool dontClose = false; #ifdef DEBUG - const bool createDotFile = JitConfig.JitDumpFgDot() != 0; - const bool includeEH = (JitConfig.JitDumpFgEH() != 0) && !compIsForInlining(); - const bool includeLoops = (JitConfig.JitDumpFgLoops() != 0) && !compIsForInlining(); - // The loop table is not well maintained after the optimization phases, but there is no single point at which - // it is declared invalid. For now, refuse to add loop information starting at the rationalize phase, to - // avoid asserts. - const bool includeOldLoops = - (JitConfig.JitDumpFgOldLoops() != 0) && !compIsForInlining() && (phase < PHASE_RATIONALIZE); + const bool createDotFile = JitConfig.JitDumpFgDot() != 0; + const bool includeEH = (JitConfig.JitDumpFgEH() != 0) && !compIsForInlining(); + const bool includeLoops = (JitConfig.JitDumpFgLoops() != 0) && !compIsForInlining(); const bool constrained = JitConfig.JitDumpFgConstrained() != 0; const bool useBlockId = JitConfig.JitDumpFgBlockID() != 0; const bool displayBlockFlags = JitConfig.JitDumpFgBlockFlags() != 0; #else // !DEBUG - const bool createDotFile = true; - const bool includeEH = false; - const bool includeLoops = false; - const bool constrained = true; - const bool useBlockId = false; - const bool displayBlockFlags = false; + const bool createDotFile = true; + const bool includeEH = false; + const bool includeLoops = false; + const bool constrained = true; + const bool useBlockId = false; + const bool displayBlockFlags = false; #endif // !DEBUG FILE* fgxFile = fgOpenFlowGraphFile(&dontClose, phase, pos, createDotFile ? "dot" : "fgx"); @@ -1203,7 +1198,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) } } - if ((includeEH && (compHndBBtabCount > 0)) || (includeOldLoops && (optLoopCount > 0))) + if (includeEH && (compHndBBtabCount > 0)) { // Generate something like: // subgraph cluster_0 { @@ -1221,8 +1216,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) // Thus, the subgraphs need to be nested to show the region nesting. // // The EH table is in order, top-to-bottom, most nested to least nested where - // there is a parent/child relationship. The loop table the opposite: it is - // in order from the least nested to most nested. + // there is a parent/child relationship. // // Build a region tree, collecting all the regions we want to display, // and then walk it to emit the regions. @@ -1235,7 +1229,6 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) { Root, EH, - Loop }; private: @@ -1424,8 +1417,6 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) return "Root"; case RegionType::EH: return "EH"; - case RegionType::Loop: - return "Loop"; default: return "UNKNOWN"; } @@ -1566,8 +1557,6 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) { case RegionType::EH: return "red"; - case RegionType::Loop: - return "blue"; default: return "black"; } @@ -1703,46 +1692,6 @@ bool Compiler::fgDumpFlowGraph(Phases phase, PhasePosition pos) } } - // Add regions for the loops. Note that loops are assumed to be contiguous from `lpTop` to `lpBottom`. - - if (includeOldLoops) - { -#ifdef DEBUG - const bool displayLoopFlags = JitConfig.JitDumpFgLoopFlags() != 0; -#else // !DEBUG - const bool displayLoopFlags = false; -#endif // !DEBUG - - char name[30]; - for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) - { - const LoopDsc& loop = optLoopTable[loopNum]; - if (loop.lpIsRemoved()) - { - continue; - } - - sprintf_s(name, sizeof(name), FMT_LP, loopNum); - - if (displayLoopFlags) - { - // Display a very few, useful, loop flags - strcat_s(name, sizeof(name), " ["); - if (loop.lpFlags & LoopFlags::LPFLG_ITER) - { - strcat_s(name, sizeof(name), "I"); - } - if (loop.lpFlags & LoopFlags::LPFLG_HAS_PREHEAD) - { - strcat_s(name, sizeof(name), "P"); - } - strcat_s(name, sizeof(name), "]"); - } - - rgnGraph.Insert(name, RegionGraph::RegionType::Loop, loop.lpTop, loop.lpBottom); - } - } - // All the regions have been added. Now, output them. DBEXEC(verbose, rgnGraph.Dump()); INDEBUG(rgnGraph.Verify()); @@ -1965,18 +1914,6 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 * printf(" "); - // - // Display natural loop number - // - if (block->bbNatLoopNum == BasicBlock::NOT_IN_LOOP) - { - printf(" "); - } - else - { - printf("%2d ", block->bbNatLoopNum); - } - // // Display block IL range // @@ -2347,7 +2284,7 @@ void Compiler::fgDispBasicBlocks(BasicBlock* firstBlock, BasicBlock* lastBlock, padWidth, "------------", ibcColWidth, "------------", maxBlockNumWidth, "----"); - printf("BBnum %*sBBid ref try hnd %s weight %*s%s lp [IL range] [jump]%*s [EH region] [flags]\n", + printf("BBnum %*sBBid ref try hnd %s weight %*s%s [IL range] [jump]%*s [EH region] [flags]\n", padWidth, "", (fgPredsComputed ? "preds " : " "), @@ -3208,7 +3145,7 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef #ifndef JIT32_GCENCODER copiedForGenericsCtxt = ((info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0); #else // JIT32_GCENCODER - copiedForGenericsCtxt = false; + copiedForGenericsCtxt = false; #endif // JIT32_GCENCODER // This if only in support of the noway_asserts it contains. @@ -4720,429 +4657,22 @@ void Compiler::fgDebugCheckSsa() } //------------------------------------------------------------------------------ -// fgDebugCheckLoopTable: checks that the loop table is valid. -// - If the method has natural loops, the loop table is not null -// - Loop `top` must come before `bottom`. -// - Loop `entry` must be between `top` and `bottom`. -// - Children loops of a loop are disjoint. -// - All basic blocks with loop numbers set have a corresponding loop in the table -// - All basic blocks without a loop number are not in a loop -// - All parents of the loop with the block contain that block -// - If optLoopsRequirePreHeaders is true, the loop has a pre-header -// - If the loop has a pre-header, it is valid -// - The loop flags are valid -// - no loop shares `top` with any of its children -// - no loop shares `bottom` with the header of any of its siblings -// - no top-entry loop has a predecessor that comes from outside the loop other than from lpHead +// fgDebugCheckLoops: Checks that all loops are canonicalized as expected. // -void Compiler::fgDebugCheckLoopTable() +void Compiler::fgDebugCheckLoops() { - if ((m_loops != nullptr) && optLoopsRequirePreHeaders) + if (m_loops == nullptr) { - for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) - { - assert(loop->EntryEdges().size() == 1); - assert(loop->EntryEdge(0)->getSourceBlock()->KindIs(BBJ_ALWAYS)); - } - } - -#ifdef DEBUG - if (!optLoopTableValid) - { - JITDUMP("*************** In fgDebugCheckLoopTable: loop table not valid\n"); return; } - JITDUMP("*************** In fgDebugCheckLoopTable\n"); -#endif // DEBUG - - if (optLoopCount > 0) - { - assert(optLoopTable != nullptr); - } - - // Build a mapping from existing block list number (bbNum) to the block number it would be after the - // blocks are renumbered. This allows making asserts about the relative ordering of blocks using block number - // without actually renumbering the blocks, which would affect non-DEBUG code paths. Note that there may be - // `blockNumMap[bbNum] == 0` if the `bbNum` block was deleted and blocks haven't been renumbered since - // the deletion. - - unsigned bbNumMax = fgBBNumMax; - - // blockNumMap[old block number] => new block number - size_t blockNumBytes = (bbNumMax + 1) * sizeof(unsigned); - unsigned* blockNumMap = (unsigned*)_alloca(blockNumBytes); - memset(blockNumMap, 0, blockNumBytes); - - unsigned newBBnum = 1; - for (BasicBlock* const block : Blocks()) - { - if (!block->HasFlag(BBF_REMOVED)) - { - assert(1 <= block->bbNum && block->bbNum <= bbNumMax); - assert(blockNumMap[block->bbNum] == 0); // If this fails, we have two blocks with the same block number. - blockNumMap[block->bbNum] = newBBnum++; - } - } - - struct MappedChecks + if (optLoopsRequirePreHeaders) { - static bool lpWellFormed(const unsigned* blockNumMap, const LoopDsc* loop) - { - return (blockNumMap[loop->lpTop->bbNum] <= blockNumMap[loop->lpEntry->bbNum]) && - (blockNumMap[loop->lpEntry->bbNum] <= blockNumMap[loop->lpBottom->bbNum]) && - ((blockNumMap[loop->lpHead->bbNum] < blockNumMap[loop->lpTop->bbNum]) || - (blockNumMap[loop->lpHead->bbNum] > blockNumMap[loop->lpBottom->bbNum])); - } - - static bool lpContains(const unsigned* blockNumMap, const LoopDsc* loop, const BasicBlock* blk) - { - return (blockNumMap[loop->lpTop->bbNum] <= blockNumMap[blk->bbNum]) && - (blockNumMap[blk->bbNum] <= blockNumMap[loop->lpBottom->bbNum]); - } - static bool lpContains(const unsigned* blockNumMap, - const LoopDsc* loop, - const BasicBlock* top, - const BasicBlock* bottom) - { - return (blockNumMap[loop->lpTop->bbNum] <= blockNumMap[top->bbNum]) && - (blockNumMap[bottom->bbNum] < blockNumMap[loop->lpBottom->bbNum]); - } - static bool lpContains(const unsigned* blockNumMap, const LoopDsc* loop, const LoopDsc& lp2) - { - return lpContains(blockNumMap, loop, lp2.lpTop, lp2.lpBottom); - } - - static bool lpContainedBy(const unsigned* blockNumMap, - const LoopDsc* loop, - const BasicBlock* top, - const BasicBlock* bottom) - { - return (blockNumMap[top->bbNum] <= blockNumMap[loop->lpTop->bbNum]) && - (blockNumMap[loop->lpBottom->bbNum] < blockNumMap[bottom->bbNum]); - } - static bool lpContainedBy(const unsigned* blockNumMap, const LoopDsc* loop, const LoopDsc& lp2) - { - return lpContainedBy(blockNumMap, loop, lp2.lpTop, lp2.lpBottom); - } - - static bool lpDisjoint(const unsigned* blockNumMap, - const LoopDsc* loop, - const BasicBlock* top, - const BasicBlock* bottom) - { - return (blockNumMap[bottom->bbNum] < blockNumMap[loop->lpTop->bbNum]) || - (blockNumMap[loop->lpBottom->bbNum] < blockNumMap[top->bbNum]); - } - static bool lpDisjoint(const unsigned* blockNumMap, const LoopDsc* loop, const LoopDsc& lp2) - { - return lpDisjoint(blockNumMap, loop, lp2.lpTop, lp2.lpBottom); - } - - // Like Disjoint, but also checks lpHead - // - static bool lpFullyDisjoint(const unsigned* blockNumMap, const LoopDsc* loop, const LoopDsc& lp2) - { - return lpDisjoint(blockNumMap, loop, lp2.lpTop, lp2.lpBottom) && - !lpContains(blockNumMap, loop, lp2.lpHead) && !lpContains(blockNumMap, &lp2, loop->lpHead); - } - - // If a top-entry loop, lpHead must be only non-loop pred of lpTop - static bool lpHasWellFormedBackedges(const unsigned* blockNumMap, const LoopDsc* loop) - { - if (loop->lpTop != loop->lpEntry) - { - // not top-entry, assume ok for now - return true; - } - - bool foundHead = false; - for (BasicBlock* const pred : loop->lpTop->PredBlocks()) - { - if (pred == loop->lpHead) - { - foundHead = true; - continue; - } - - if (!lpContains(blockNumMap, loop, pred)) - { - return false; - } - } - - return foundHead; - } - }; - - // Check the loop table itself. - - int preHeaderCount = 0; - - for (unsigned i = 0; i < optLoopCount; i++) - { - const LoopDsc& loop = optLoopTable[i]; - - // Ignore removed loops - if (loop.lpIsRemoved()) - { - continue; - } - - assert(loop.lpHead != nullptr); - assert(loop.lpTop != nullptr); - assert(loop.lpEntry != nullptr); - assert(loop.lpBottom != nullptr); - - assert(MappedChecks::lpWellFormed(blockNumMap, &loop)); - assert(MappedChecks::lpHasWellFormedBackedges(blockNumMap, &loop)); - - if (loop.lpExitCnt == 1) - { - assert(loop.lpExit != nullptr); - assert(MappedChecks::lpContains(blockNumMap, &loop, loop.lpExit)); - } - else - { - assert(loop.lpExit == nullptr); - } - - if (loop.lpParent == BasicBlock::NOT_IN_LOOP) - { - // This is a top-level loop. - - // Verify all top-level loops are fully disjoint. We don't have a list of just these (such as a - // top-level pseudo-loop entry with a list of all top-level lists), so we have to iterate - // over the entire loop table. - for (unsigned j = 0; j < optLoopCount; j++) - { - if (i == j) - { - // Don't compare against ourselves. - continue; - } - const LoopDsc& otherLoop = optLoopTable[j]; - if (otherLoop.lpIsRemoved()) - { - continue; - } - if (otherLoop.lpParent != BasicBlock::NOT_IN_LOOP) - { - // Only consider top-level loops - continue; - } - assert(MappedChecks::lpFullyDisjoint(blockNumMap, &loop, otherLoop)); - } - } - else - { - // This is not a top-level loop - - assert(loop.lpParent != BasicBlock::NOT_IN_LOOP); - assert(loop.lpParent < optLoopCount); - assert(loop.lpParent < i); // outer loops come before inner loops in the table - - const LoopDsc& parentLoop = optLoopTable[loop.lpParent]; - assert(!parentLoop.lpIsRemoved()); // don't allow removed parent loop? - - // Either there is no sibling or it should not be marked REMOVED. - assert((loop.lpSibling == BasicBlock::NOT_IN_LOOP) || !optLoopTable[loop.lpSibling].lpIsRemoved()); - - // Either there is no child or it should not be marked REMOVED. - assert((loop.lpChild == BasicBlock::NOT_IN_LOOP) || !optLoopTable[loop.lpChild].lpIsRemoved()); - - assert(MappedChecks::lpContainedBy(blockNumMap, &loop, optLoopTable[loop.lpParent])); - } - - if (loop.lpChild != BasicBlock::NOT_IN_LOOP) - { - // Verify all child loops are contained in the parent loop. - for (unsigned child = loop.lpChild; // - child != BasicBlock::NOT_IN_LOOP; // - child = optLoopTable[child].lpSibling) - { - assert(child < optLoopCount); - assert(i < child); // outer loops come before inner loops in the table - const LoopDsc& childLoop = optLoopTable[child]; - assert(!childLoop.lpIsRemoved()); - assert(MappedChecks::lpContains(blockNumMap, &loop, childLoop)); - assert(childLoop.lpParent == i); - } - - // Verify all child loops are fully disjoint. - for (unsigned child = loop.lpChild; // - child != BasicBlock::NOT_IN_LOOP; // - child = optLoopTable[child].lpSibling) - { - const LoopDsc& childLoop = optLoopTable[child]; - assert(!childLoop.lpIsRemoved()); - for (unsigned child2 = optLoopTable[child].lpSibling; // - child2 != BasicBlock::NOT_IN_LOOP; // - child2 = optLoopTable[child2].lpSibling) - { - const LoopDsc& child2Loop = optLoopTable[child2]; - assert(!child2Loop.lpIsRemoved()); - assert(MappedChecks::lpFullyDisjoint(blockNumMap, &childLoop, child2Loop)); - } - } - - // Verify no child shares lpTop with its parent. - for (unsigned child = loop.lpChild; // - child != BasicBlock::NOT_IN_LOOP; // - child = optLoopTable[child].lpSibling) - { - const LoopDsc& childLoop = optLoopTable[child]; - assert(!childLoop.lpIsRemoved()); - assert(loop.lpTop != childLoop.lpTop); - } - } - - if (optLoopsRequirePreHeaders) - { - assert((loop.lpFlags & LPFLG_HAS_PREHEAD) != 0); - } - - // If the loop has a pre-header, ensure the pre-header form is correct. - if ((loop.lpFlags & LPFLG_HAS_PREHEAD) != 0) - { - ++preHeaderCount; - - BasicBlock* h = loop.lpHead; - assert(h->HasFlag(BBF_LOOP_PREHEADER)); - - // The pre-header can only be BBJ_ALWAYS and must enter the loop. - BasicBlock* e = loop.lpEntry; - assert(h->KindIs(BBJ_ALWAYS)); - assert(h->TargetIs(e)); - - // The entry block has a single non-loop predecessor, and it is the pre-header. - for (BasicBlock* const predBlock : e->PredBlocks()) - { - if (predBlock != h) - { - assert(MappedChecks::lpContains(blockNumMap, &loop, predBlock)); - } - } - - loop.lpValidatePreHeader(); - } - - // Check the flags. - // Note that the various limit flags are only used when LPFLG_ITER is set, but they are set first, - // separately, and only if everything works out is LPFLG_ITER set. If LPFLG_ITER is NOT set, the - // individual flags are not un-set (arguably, they should be). - - // Only one of the `limit` flags can be set. (Note that LPFLG_SIMD_LIMIT is a "sub-flag" that can be - // set when LPFLG_CONST_LIMIT is set.) - assert(genCountBits((unsigned)(loop.lpFlags & (LPFLG_VAR_LIMIT | LPFLG_CONST_LIMIT | LPFLG_ARRLEN_LIMIT))) <= - 1); - - // LPFLG_SIMD_LIMIT can only be set if LPFLG_CONST_LIMIT is set. - if (loop.lpFlags & LPFLG_SIMD_LIMIT) - { - assert(loop.lpFlags & LPFLG_CONST_LIMIT); - } - - if (loop.lpFlags & LPFLG_CONST_INIT) - { - assert(loop.lpInitBlock != nullptr); - } - - if (loop.lpFlags & LPFLG_ITER) - { - loop.VERIFY_lpIterTree(); - loop.VERIFY_lpTestTree(); - } - - // If we have dominators, we check more things: - // 1. The pre-header dominates the entry (if pre-headers are required). - // 2. The entry dominates the exit. - // 3. The IDom tree from the exit reaches the entry. - if (m_domTree != nullptr) - { - if (optLoopsRequirePreHeaders) - { - assert(m_domTree->Dominates(loop.lpHead, loop.lpEntry)); - } - - if (loop.lpExitCnt == 1) - { - assert(loop.lpExit != nullptr); - assert(m_domTree->Dominates(loop.lpEntry, loop.lpExit)); - - BasicBlock* cur = loop.lpExit; - while ((cur != nullptr) && (cur != loop.lpEntry)) - { - assert(m_domTree->Dominates(cur, loop.lpExit)); - cur = cur->bbIDom; - } - assert(cur == loop.lpEntry); // We must be able to reach the entry from the exit via the IDom tree. - } - } - } - - // Check basic blocks for loop annotations. - - for (BasicBlock* const block : Blocks()) - { - if (optLoopCount == 0) - { - assert(block->bbNatLoopNum == BasicBlock::NOT_IN_LOOP); - continue; - } - - // Walk the loop table and find the first loop that contains our block. - // It should be the innermost one. - int loopNum = BasicBlock::NOT_IN_LOOP; - for (int i = optLoopCount - 1; i >= 0; i--) - { - // Ignore removed loops - if (optLoopTable[i].lpIsRemoved()) - { - continue; - } - // Does this loop contain our block? - if (MappedChecks::lpContains(blockNumMap, &optLoopTable[i], block)) - { - loopNum = i; - break; - } - } - - // If there is at least one loop that contains this block... - if (loopNum != BasicBlock::NOT_IN_LOOP) - { - // ...it must be the one pointed to by bbNatLoopNum. - assert(block->bbNatLoopNum == loopNum); - - // TODO: We might want the following assert, but there are cases where we don't move all - // return blocks out of the loop. - // Return blocks are not allowed inside a loop; they should have been moved elsewhere. - // assert(!block->KindIs(BBJ_RETURN)); - } - else - { - // Otherwise, this block should not point to a loop. - assert(block->bbNatLoopNum == BasicBlock::NOT_IN_LOOP); - } - - // All loops that contain the innermost loop with this block must also contain this block. - while (loopNum != BasicBlock::NOT_IN_LOOP) - { - assert(MappedChecks::lpContains(blockNumMap, &optLoopTable[loopNum], block)); - loopNum = optLoopTable[loopNum].lpParent; - } - - if (block->HasFlag(BBF_LOOP_PREHEADER)) + for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) { - // Note that the bbNatLoopNum will not point to the loop where this is a pre-header, since bbNatLoopNum - // is only set on the blocks from `top` to `bottom`, and `head` is outside that. - --preHeaderCount; + assert(loop->EntryEdges().size() == 1); + assert(loop->EntryEdge(0)->getSourceBlock()->KindIs(BBJ_ALWAYS)); } } - - // Verify that the number of loops marked as having pre-headers is the same as the number of blocks - // with the pre-header flag set. - assert(preHeaderCount == 0); } //------------------------------------------------------------------------------ diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 16ef174b53c63f..25afba806486a9 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -1356,11 +1356,6 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) block->RemoveFlags(BBF_NONE_QUIRK); } - if (optLoopTableValid) - { - fgUpdateLoopsAfterCompacting(block, bNext); - } - #if DEBUG if (verbose && 0) { @@ -1378,66 +1373,6 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) #endif // DEBUG } -//------------------------------------------------------------- -// fgUpdateLoopsAfterCompacting: Update the loop table after block compaction. -// -// Arguments: -// block - target of compaction. -// bNext - bbNext of `block`. This block has been removed. -// -void Compiler::fgUpdateLoopsAfterCompacting(BasicBlock* block, BasicBlock* bNext) -{ - /* Check if the removed block is not part the loop table */ - noway_assert(bNext); - - for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) - { - /* Some loops may have been already removed by - * loop unrolling or conditional folding */ - - if (optLoopTable[loopNum].lpIsRemoved()) - { - continue; - } - - /* Check the loop head (i.e. the block preceding the loop) */ - - if (optLoopTable[loopNum].lpHead == bNext) - { - optLoopTable[loopNum].lpHead = block; - } - - /* Check the loop bottom */ - - if (optLoopTable[loopNum].lpBottom == bNext) - { - optLoopTable[loopNum].lpBottom = block; - } - - /* Check the loop exit */ - - if (optLoopTable[loopNum].lpExit == bNext) - { - noway_assert(optLoopTable[loopNum].lpExitCnt == 1); - optLoopTable[loopNum].lpExit = block; - } - - /* Check the loop entry */ - - if (optLoopTable[loopNum].lpEntry == bNext) - { - optLoopTable[loopNum].lpEntry = block; - } - - /* Check the loop top */ - - if (optLoopTable[loopNum].lpTop == bNext) - { - optLoopTable[loopNum].lpTop = block; - } - } -} - //------------------------------------------------------------- // fgUnreachableBlock: Remove a block when it is unreachable. // @@ -1500,9 +1435,6 @@ void Compiler::fgUnreachableBlock(BasicBlock* block) noway_assert(block->bbStmtList == nullptr); } - // Next update the loop table and bbWeights - optUpdateLoopsBeforeRemoveBlock(block); - // Mark the block as removed block->SetFlags(BBF_REMOVED); @@ -4798,10 +4730,6 @@ PhaseStatus Compiler::fgUpdateFlowGraphPhase() constexpr bool isPhase = true; const bool madeChanges = fgUpdateFlowGraph(doTailDup, isPhase); - // The loop table is no longer valid. - optLoopTableValid = false; - optLoopsRequirePreHeaders = false; - return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } @@ -5157,12 +5085,6 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) /* Mark the block as removed */ bNext->SetFlags(BBF_REMOVED); - if (optLoopTableValid) - { - // Update the loop table if we removed the bottom of a loop, for example. - fgUpdateLoopsAfterCompacting(block, bNext); - } - // If this is the first Cold basic block update fgFirstColdBlock if (bNext->IsFirstColdBlock(this)) { diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index e65d161b8813a1..79ee2e97881edf 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -266,16 +266,8 @@ BasicBlock* Compiler::fgCreateGCPoll(GCPollType pollType, BasicBlock* block) // I want to create: // top -> poll -> bottom (lexically) // so that we jump over poll to get to bottom. - BasicBlock* top = block; - unsigned char lpIndexFallThrough = BasicBlock::NOT_IN_LOOP; - - BBKinds oldJumpKind = top->GetKind(); - unsigned char lpIndex = top->bbNatLoopNum; - - if (oldJumpKind == BBJ_COND) - { - lpIndexFallThrough = top->GetFalseTarget()->bbNatLoopNum; - } + BasicBlock* top = block; + BBKinds oldJumpKind = top->GetKind(); BasicBlock* poll = fgNewBBafter(BBJ_ALWAYS, top, true); bottom = fgNewBBafter(top->GetKind(), poll, true); @@ -302,24 +294,6 @@ BasicBlock* Compiler::fgCreateGCPoll(GCPollType pollType, BasicBlock* block) // Mark Poll as rarely run. poll->bbSetRunRarely(); - if (optLoopTableValid) - { - poll->bbNatLoopNum = lpIndex; // Set the bbNatLoopNum in case we are in a loop - - bottom->bbNatLoopNum = lpIndex; // Set the bbNatLoopNum in case we are in a loop - if (lpIndex != BasicBlock::NOT_IN_LOOP) - { - // Set the new lpBottom in the natural loop table - optLoopTable[lpIndex].lpBottom = bottom; - } - - if (lpIndexFallThrough != BasicBlock::NOT_IN_LOOP) - { - // Set the new lpHead in the natural loop table - optLoopTable[lpIndexFallThrough].lpHead = bottom; - } - } - // Add the GC_CALL node to Poll. Statement* pollStmt = fgNewStmtAtEnd(poll, call); if (fgNodeThreading != NodeThreading::None) @@ -4568,24 +4542,13 @@ void FlowGraphNaturalLoop::Dump(FlowGraphNaturalLoop* loop) return; } - // Display: LOOP# (old LOOP#) / header / parent loop# / blocks / entry edges / exit edges / back edges + // Display: LOOP# / header / parent loop# / blocks / entry edges / exit edges / back edges // Blocks can compacted be "[top .. bottom]" if lexically adjacent and no non-loop blocks in // the range. Otherwise, print a verbose list of blocks. Compiler* comp = loop->GetDfsTree()->GetCompiler(); printf(FMT_LP, loop->GetIndex()); - // We might want to print out the old loop number using something like: - // - // if (comp->m_newToOldLoop[loop->GetIndex()] != nullptr) - // { - // printf(" (old: " FMT_LP ")", (unsigned)(comp->m_newToOldLoop[loop->GetIndex()] - comp->optLoopTable)); - // } - // - // However, not all callers of FlowGraphNaturalLoops::Find update m_newToOldLoop -- only - // Compiler::optFindNewLoops() does that. This dumper should work with any construction of - // FlowGraphNaturalLoops. - printf(" header: " FMT_BB, loop->GetHeader()->bbNum); if (loop->GetParent() != nullptr) { @@ -4786,7 +4749,7 @@ void FlowGraphNaturalLoop::Dump(FlowGraphNaturalLoop* loop) /* static */ void FlowGraphNaturalLoops::Dump(FlowGraphNaturalLoops* loops) { - printf("\n*************** (New) Natural loop graph\n"); + printf("\n*************** Natural loop graph\n"); if (loops == nullptr) { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b8f109c68b43a8..ef4dd9be146911 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -453,7 +453,6 @@ enum GenTreeFlags : unsigned int GTF_LIVENESS_MASK = GTF_VAR_DEF | GTF_VAR_USEASG | GTF_VAR_DEATH_MASK, - GTF_VAR_ITERATOR = 0x01000000, // GT_LCL_VAR -- this is a iterator reference in the loop condition GTF_VAR_MOREUSES = 0x00800000, // GT_LCL_VAR -- this node has additional uses, for example due to cloning GTF_VAR_CONTEXT = 0x00400000, // GT_LCL_VAR -- this node is part of a runtime lookup GTF_VAR_EXPLICIT_INIT = 0x00200000, // GT_LCL_VAR -- this node is an "explicit init" store. Valid until rationalization. diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index ac2a65b33ef95c..ad3b20bc7ecc6f 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -450,17 +450,6 @@ bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stm fallbackBb->inheritWeightPercentage(nullcheckBb, 20); } - // - // Update loop info - // - nullcheckBb->bbNatLoopNum = prevBb->bbNatLoopNum; - fastPathBb->bbNatLoopNum = prevBb->bbNatLoopNum; - fallbackBb->bbNatLoopNum = prevBb->bbNatLoopNum; - if (needsSizeCheck) - { - sizeCheckBb->bbNatLoopNum = prevBb->bbNatLoopNum; - } - // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); assert(BasicBlock::sameEHRegion(prevBb, nullcheckBb)); @@ -694,13 +683,6 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St fgAddRefPred(tlsRootNullCondBB, prevBb); prevBb->SetTarget(tlsRootNullCondBB); - // - // Update loop info if loop table is known to be valid - // - tlsRootNullCondBB->bbNatLoopNum = prevBb->bbNatLoopNum; - fastPathBb->bbNatLoopNum = prevBb->bbNatLoopNum; - fallbackBb->bbNatLoopNum = prevBb->bbNatLoopNum; - // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); assert(BasicBlock::sameEHRegion(prevBb, tlsRootNullCondBB)); @@ -1080,14 +1062,6 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* // fallback will just execute first time fallbackBb->bbSetRunRarely(); - // - // Update loop info if loop table is known to be valid - // - maxThreadStaticBlocksCondBB->bbNatLoopNum = prevBb->bbNatLoopNum; - threadStaticBlockNullCondBB->bbNatLoopNum = prevBb->bbNatLoopNum; - fastPathBb->bbNatLoopNum = prevBb->bbNatLoopNum; - fallbackBb->bbNatLoopNum = prevBb->bbNatLoopNum; - // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); assert(BasicBlock::sameEHRegion(prevBb, maxThreadStaticBlocksCondBB)); @@ -1449,13 +1423,6 @@ bool Compiler::fgExpandStaticInitForCall(BasicBlock** pBlock, Statement* stmt, G isInitedBb->inheritWeight(prevBb); helperCallBb->bbSetRunRarely(); - // - // Update loop info if loop table is known to be valid - // - - isInitedBb->bbNatLoopNum = prevBb->bbNatLoopNum; - helperCallBb->bbNatLoopNum = prevBb->bbNatLoopNum; - // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); assert(BasicBlock::sameEHRegion(prevBb, isInitedBb)); @@ -1794,12 +1761,6 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock, fastpathBb->inheritWeight(lengthCheckBb); block->inheritWeight(prevBb); - // - // Update bbNatLoopNum for all new blocks - // - lengthCheckBb->bbNatLoopNum = prevBb->bbNatLoopNum; - fastpathBb->bbNatLoopNum = prevBb->bbNatLoopNum; - // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); assert(BasicBlock::sameEHRegion(prevBb, lengthCheckBb)); @@ -2101,12 +2062,8 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, lastBb->inheritWeight(firstBb); // - // Update bbNatLoopNum for all new blocks and validate EH regions + // Validate EH regions // - nullcheckBb->bbNatLoopNum = firstBb->bbNatLoopNum; - fallbackBb->bbNatLoopNum = firstBb->bbNatLoopNum; - typeCheckBb->bbNatLoopNum = firstBb->bbNatLoopNum; - typeCheckSucceedBb->bbNatLoopNum = firstBb->bbNatLoopNum; assert(BasicBlock::sameEHRegion(firstBb, lastBb)); assert(BasicBlock::sameEHRegion(firstBb, nullcheckBb)); assert(BasicBlock::sameEHRegion(firstBb, fallbackBb)); diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 82c1a1a0697aeb..50afc5c7cf1c06 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -220,11 +220,9 @@ CONFIG_STRING(JitDumpFgPhase, W("JitDumpFgPhase")) // Phase-based Xml/Dot flowgr // phases CONFIG_STRING(JitDumpFgPrePhase, W("JitDumpFgPrePhase")) // Same as JitDumpFgPhase, but specifies to dump pre-phase, not post-phase. -CONFIG_INTEGER(JitDumpFgDot, W("JitDumpFgDot"), 1) // 0 == dump XML format; non-zero == dump DOT format -CONFIG_INTEGER(JitDumpFgEH, W("JitDumpFgEH"), 0) // 0 == no EH regions; non-zero == include EH regions -CONFIG_INTEGER(JitDumpFgLoops, W("JitDumpFgLoops"), 0) // 0 == no loop regions; non-zero == include loop regions -CONFIG_INTEGER(JitDumpFgOldLoops, W("JitDumpFgOldLoops"), 0) // 0 == no old loop regions; non-zero == include old loop - // regions +CONFIG_INTEGER(JitDumpFgDot, W("JitDumpFgDot"), 1) // 0 == dump XML format; non-zero == dump DOT format +CONFIG_INTEGER(JitDumpFgEH, W("JitDumpFgEH"), 0) // 0 == no EH regions; non-zero == include EH regions +CONFIG_INTEGER(JitDumpFgLoops, W("JitDumpFgLoops"), 0) // 0 == no loop regions; non-zero == include loop regions CONFIG_INTEGER(JitDumpFgConstrained, W("JitDumpFgConstrained"), 1) // 0 == don't constrain to mostly linear layout; // non-zero == force mostly lexical block diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index fc9703feafcad2..730eb91738f670 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -862,7 +862,6 @@ BasicBlock* LoopCloneContext::CondToStmtInBlock(Compiler* { BasicBlock* newBlk = comp->fgNewBBafter(BBJ_COND, insertAfter, /*extendRegion*/ true, slowPreheader); newBlk->inheritWeight(insertAfter); - newBlk->bbNatLoopNum = insertAfter->bbNatLoopNum; JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", newBlk->bbNum, newBlk->GetTrueTarget()->bbNum); comp->fgAddRefPred(newBlk->GetTrueTarget(), newBlk); @@ -897,7 +896,6 @@ BasicBlock* LoopCloneContext::CondToStmtInBlock(Compiler* { BasicBlock* newBlk = comp->fgNewBBafter(BBJ_COND, insertAfter, /*extendRegion*/ true, slowPreheader); newBlk->inheritWeight(insertAfter); - newBlk->bbNatLoopNum = insertAfter->bbNatLoopNum; JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", newBlk->bbNum, newBlk->GetTrueTarget()->bbNum); comp->fgAddRefPred(newBlk->GetTrueTarget(), newBlk); @@ -2098,9 +2096,11 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex // TODO: scale the pred edges of `blk`? // If the loop we're cloning contains nested loops, we need to clear the pre-header bit on - // any nested loop pre-header blocks, since they will no longer be loop pre-headers. (This is because - // we don't add the slow loop or its child loops to the loop table. It would be simplest to - // just re-build the loop table if we want to enable loop optimization of the slow path loops.) + // any nested loop pre-header blocks, since they will no longer be loop pre-headers. + // + // TODO-Cleanup: BBF_LOOP_PREHEADER can be removed; we do not attempt + // to keep it up to date anymore when we do FG changes. + // if (newBlk->HasFlag(BBF_LOOP_PREHEADER)) { JITDUMP("Removing BBF_LOOP_PREHEADER flag from nested cloned loop block " FMT_BB "\n", newBlk->bbNum); diff --git a/src/coreclr/jit/loopcloning.h b/src/coreclr/jit/loopcloning.h index 9279bc783a9ecf..2333d491764cd7 100644 --- a/src/coreclr/jit/loopcloning.h +++ b/src/coreclr/jit/loopcloning.h @@ -135,10 +135,7 @@ exception occurs. Preconditions - 1. Loop detection has completed and the loop table is populated. - - 2. The loops that will be considered are the ones with the LPFLG_ITER flag: - "for ( ; test_condition(); i++)" + Loop detection has completed and the Compiler::m_loops is populated. Limitations @@ -147,11 +144,7 @@ exception occurs. is "hard".) There are a few other EH-related edge conditions that also cause us to reject cloning. - 2. If the loop contains RETURN blocks, and cloning those would push us over the maximum - number of allowed RETURN blocks in the function (either due to GC info encoding limitations - or otherwise), we reject cloning. - - 3. Loop increment must be `i += 1` + 2. Loop increment must be `i += 1` 4. Loop test must be `i < x` or `i <= x` where `x` is a constant, a variable, or `a.Length` for array `a` @@ -168,10 +161,6 @@ exception occurs. For non-constant (or not found) iterator variable `i` initialization, we add a dynamic check that `i >= 0`. Constant initializations can be checked statically. - 9. The cloned loop (the slow path) is not added to the loop table, meaning certain - downstream optimization passes do not see them. See - https://github.com/dotnet/runtime/issues/43713. - Assumptions 1. The assumption is that the optimization candidates collected during the @@ -181,7 +170,7 @@ exception occurs. collect additional information at the same time as identifying the optimization candidates. This later helps us to perform the optimizations during actual cloning. - 2. All loop cloning choice conditions will automatically be "AND"-ed. These are bitwise AND operations. + 2. All loop cloning choice conditions will automatically be "AND"-ed. 3. Perform short circuit AND for (array != null) side effect check before hoisting (limit <= a.length) check. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1bf3288fa7c3c2..33428d5fd5a11f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13220,24 +13220,14 @@ Compiler::FoldResult Compiler::fgFoldConditional(BasicBlock* block) if (cond->AsIntCon()->gtIconVal != 0) { - /* JTRUE 1 - transform the basic block into a BBJ_ALWAYS */ + // JTRUE 1 - transform the basic block into a BBJ_ALWAYS bTaken = block->GetTrueTarget(); bNotTaken = block->GetFalseTarget(); block->SetKind(BBJ_ALWAYS); } else { - /* Unmark the loop if we are removing a backwards branch */ - /* dest block must also be marked as a loop head and */ - /* We must be able to reach the backedge block */ - if (optLoopTableValid && block->GetTrueTarget()->isLoopHead() && - (block->GetTrueTarget()->bbNum <= block->bbNum) && - m_reachabilitySets->CanReach(block->GetTrueTarget(), block)) - { - optUnmarkLoopBlocks(block->GetTrueTarget(), block); - } - - /* JTRUE 0 - transform the basic block into a BBJ_ALWAYS */ + // JTRUE 0 - transform the basic block into a BBJ_ALWAYS bTaken = block->GetFalseTarget(); bNotTaken = block->GetTrueTarget(); block->SetKindAndTarget(BBJ_ALWAYS, bTaken); @@ -13336,56 +13326,6 @@ Compiler::FoldResult Compiler::fgFoldConditional(BasicBlock* block) printf("\n"); } #endif - - // Handle updates to the loop table. - // Note this is distinct from the check for BBF_LOOP_HEAD above. - // - if (optLoopTableValid) - { - for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) - { - LoopDsc& loop = optLoopTable[loopNum]; - - // Some loops may have been already removed by - // loop unrolling or conditional folding - // - if (loop.lpIsRemoved()) - { - continue; - } - - // Removed edge from bottom -> entry? - // - if ((loop.lpBottom == block) && (loop.lpEntry == bNotTaken)) - { - // This either destroyed the loop or lessened its extent. - // We currently ignore the latter. - // - if (loop.lpEntry->countOfInEdges() == 1) - { - // We removed the only backedge. - // - JITDUMP("Removing loop " FMT_LP " (from " FMT_BB " to " FMT_BB - ") -- no longer has a backedge\n\n", - loopNum, loop.lpTop->bbNum, loop.lpBottom->bbNum); - - optMarkLoopRemoved(loopNum); - } - } - - // Removed edge from head -> entry? - // - if ((loop.lpHead == block) && (loop.lpEntry == bNotTaken)) - { - // Loop is no longer reachable from outside - // - JITDUMP("Removing loop " FMT_LP " (from " FMT_BB " to " FMT_BB ") -- no longer reachable\n\n", - loopNum, loop.lpTop->bbNum, loop.lpBottom->bbNum); - - optMarkLoopRemoved(loopNum); - } - } - } } } else if (block->KindIs(BBJ_SWITCH)) diff --git a/src/coreclr/jit/optimizebools.cpp b/src/coreclr/jit/optimizebools.cpp index b96c4ae024e404..7cca29bc7dbfef 100644 --- a/src/coreclr/jit/optimizebools.cpp +++ b/src/coreclr/jit/optimizebools.cpp @@ -1188,7 +1188,7 @@ bool OptBoolsDsc::optOptimizeBoolsChkTypeCostCond() //----------------------------------------------------------------------------- // optOptimizeBoolsUpdateTrees: Fold the trees based on fold type and comparison type, -// update the edges, unlink removed blocks and update loop table +// update the edges, and unlink removed blocks // void OptBoolsDsc::optOptimizeBoolsUpdateTrees() { @@ -1340,16 +1340,6 @@ void OptBoolsDsc::optOptimizeBoolsUpdateTrees() m_comp->ehUpdateForDeletedBlock(m_b3); } - // Update loop table - if (m_comp->optLoopTableValid) - { - m_comp->fgUpdateLoopsAfterCompacting(m_b1, m_b2); - if (optReturnBlock) - { - m_comp->fgUpdateLoopsAfterCompacting(m_b1, m_b3); - } - } - // Update IL range of first block m_b1->bbCodeOffsEnd = optReturnBlock ? m_b3->bbCodeOffsEnd : m_b2->bbCodeOffsEnd; } diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 76c3662e9a799b..8e5124745e9e79 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -21,12 +21,8 @@ void Compiler::optInit() { fgHasLoops = false; - /* Initialize the # of tracked loops to 0 */ - optLoopCount = 0; - optLoopTable = nullptr; - optLoopTableValid = false; optLoopsRequirePreHeaders = false; - optCurLoopEpoch = 0; + optNumNaturalLoopsFound = 0; #ifdef DEBUG loopAlignCandidates = 0; @@ -290,1822 +286,278 @@ void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) } } -//------------------------------------------------------------------------ -// optUnmarkLoopBlocks: Unmark the blocks between 'begBlk' and 'endBlk' as part of a loop. +//---------------------------------------------------------------------------------- +// optIsLoopIncrTree: Check if loop is a tree of form v = v op const. // // Arguments: -// begBlk - first block of range. Must be marked as a loop head (BBF_LOOP_HEAD). -// endBlk - last block of range (inclusive). Must be reachable from `begBlk`. +// incr - The incr tree to be checked. // -// Operation: -// A set of blocks that were previously marked as a loop are now to be unmarked, since we have decided that -// for some reason this loop no longer exists. Basically we are just resetting the blocks bbWeight to their -// previous values. +// Return Value: +// iterVar local num if the iterVar is found, otherwise BAD_VAR_NUM. // -void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) +unsigned Compiler::optIsLoopIncrTree(GenTree* incr) { - noway_assert(begBlk->bbNum <= endBlk->bbNum); - noway_assert(begBlk->isLoopHead()); - noway_assert(!opts.MinOpts()); - - unsigned backEdgeCount = 0; - - for (BasicBlock* const predBlock : begBlk->PredBlocks()) + GenTree* incrVal; + genTreeOps updateOper; + unsigned iterVar = incr->IsLclVarUpdateTree(&incrVal, &updateOper); + if (iterVar != BAD_VAR_NUM) { - // Is this a backward edge? (from predBlock to begBlk) - if (begBlk->bbNum > predBlock->bbNum) + // We have v = v op y type node. + switch (updateOper) { - continue; + case GT_ADD: + case GT_SUB: + case GT_MUL: + case GT_RSH: + case GT_LSH: + break; + default: + return BAD_VAR_NUM; } - // We only consider back-edges that are BBJ_COND or BBJ_ALWAYS for loops. - if (!predBlock->KindIs(BBJ_COND, BBJ_ALWAYS)) + // Increment should be by a const int. + // TODO-CQ: CLONE: allow variable increments. + if ((incrVal->gtOper != GT_CNS_INT) || (incrVal->TypeGet() != TYP_INT)) { - continue; + return BAD_VAR_NUM; } - - backEdgeCount++; } - // Only unmark the loop blocks if we have exactly one loop back edge. - if (backEdgeCount != 1) - { -#ifdef DEBUG - if (verbose) - { - if (backEdgeCount > 0) - { - printf("\nNot removing loop at " FMT_BB ", due to an additional back edge", begBlk->bbNum); - } - else if (backEdgeCount == 0) - { - printf("\nNot removing loop at " FMT_BB ", due to no back edge", begBlk->bbNum); - } - } -#endif - return; - } - noway_assert(m_reachabilitySets->CanReach(begBlk, endBlk)); + return iterVar; +} -#ifdef DEBUG - if (verbose) +//---------------------------------------------------------------------------------- +// optIsLoopTestEvalIntoTemp: +// Pattern match if the test tree is computed into a tmp +// and the "tmp" is used as jump condition for loop termination. +// +// Arguments: +// testStmt - is the JTRUE statement that is of the form: jmpTrue (Vtmp != 0) +// where Vtmp contains the actual loop test result. +// newTestStmt - contains the statement that is the actual test stmt involving +// the loop iterator. +// +// Return Value: +// Returns true if a new test tree can be obtained. +// +// Operation: +// Scan if the current stmt is a jtrue with (Vtmp != 0) as condition +// Then returns the rhs for def of Vtmp as the "test" node. +// +// Note: +// This method just retrieves what it thinks is the "test" node, +// the callers are expected to verify that "iterVar" is used in the test. +// +bool Compiler::optIsLoopTestEvalIntoTemp(Statement* testStmt, Statement** newTestStmt) +{ + GenTree* test = testStmt->GetRootNode(); + + if (test->gtOper != GT_JTRUE) { - printf("\nUnmarking a loop from " FMT_BB " to " FMT_BB, begBlk->bbNum, endBlk->bbNum); + return false; } -#endif - for (BasicBlock* const curBlk : BasicBlockRangeList(begBlk, endBlk)) - { - // Stop if we go past the last block in the loop, as it may have been deleted. - if (curBlk->bbNum > endBlk->bbNum) - { - break; - } + GenTree* relop = test->gtGetOp1(); + noway_assert(relop->OperIsCompare()); - // Don't change the block weight if it's known to be rarely run. - if (curBlk->isRunRarely()) - { - continue; - } + GenTree* opr1 = relop->AsOp()->gtOp1; + GenTree* opr2 = relop->AsOp()->gtOp2; - // Don't change the block weight if it came from profile data. - if (curBlk->hasProfileWeight()) + // Make sure we have jtrue (vtmp != 0) + if ((relop->OperGet() == GT_NE) && (opr1->OperGet() == GT_LCL_VAR) && (opr2->OperGet() == GT_CNS_INT) && + opr2->IsIntegralConst(0)) + { + // Get the previous statement to get the def (rhs) of Vtmp to see + // if the "test" is evaluated into Vtmp. + Statement* prevStmt = testStmt->GetPrevStmt(); + if (prevStmt == nullptr) { - continue; + return false; } - // Don't unmark blocks that are maximum weight. - if (curBlk->isMaxBBWeight()) + GenTree* tree = prevStmt->GetRootNode(); + if (tree->OperIs(GT_STORE_LCL_VAR) && (tree->AsLclVar()->GetLclNum() == opr1->AsLclVar()->GetLclNum()) && + tree->AsLclVar()->Data()->OperIsCompare()) { - continue; + *newTestStmt = prevStmt; + return true; } + } + return false; +} - // For curBlk to be part of a loop that starts at begBlk, curBlk must be reachable from begBlk and - // (since this is a loop) begBlk must likewise be reachable from curBlk. - // - if (m_reachabilitySets->CanReach(curBlk, begBlk) && m_reachabilitySets->CanReach(begBlk, curBlk)) - { - weight_t scale = 1.0 / BB_LOOP_WEIGHT_SCALE; - - if (!m_domTree->Dominates(curBlk, endBlk)) - { - scale *= 2; - } +//---------------------------------------------------------------------------------- +// optExtractInitTestIncr: +// Extract the "init", "test" and "incr" nodes of the loop. +// +// Arguments: +// pInitBlock - [IN/OUT] *pInitBlock is the loop head block on entry, and is set to the initBlock on exit, +// if `**ppInit` is non-null. +// bottom - Loop bottom block +// top - Loop top block +// ppInit - The init stmt of the loop if found. +// ppTest - The test stmt of the loop if found. +// ppIncr - The incr stmt of the loop if found. +// +// Return Value: +// The results are put in "ppInit", "ppTest" and "ppIncr" if the method +// returns true. Returns false if the information can't be extracted. +// Extracting the `init` is optional; if one is not found, *ppInit is set +// to nullptr. Return value will never be false if `init` is not found. +// +// Operation: +// Check if the "test" stmt is last stmt in the loop "bottom". Try to find the "incr" stmt. +// Check previous stmt of "test" to get the "incr" stmt. If it is not found it could be a loop of the +// below form. +// +// +-------<-----------------<-----------+ +// | | +// v | +// BBinit(head) -> BBcond(top) -> BBLoopBody(bottom) ---^ +// +// Check if the "incr" tree is present in the loop "top" node as the last stmt. +// Also check if the "test" tree is assigned to a tmp node and the tmp is used +// in the jtrue condition. +// +// Note: +// This method just retrieves what it thinks is the "test" node, +// the callers are expected to verify that "iterVar" is used in the test. +// +bool Compiler::optExtractInitTestIncr( + BasicBlock** pInitBlock, BasicBlock* bottom, BasicBlock* top, GenTree** ppInit, GenTree** ppTest, GenTree** ppIncr) +{ + assert(pInitBlock != nullptr); + assert(ppInit != nullptr); + assert(ppTest != nullptr); + assert(ppIncr != nullptr); - curBlk->scaleBBWeight(scale); + // Check if last two statements in the loop body are the increment of the iterator + // and the loop termination test. + noway_assert(bottom->bbStmtList != nullptr); + Statement* testStmt = bottom->lastStmt(); + noway_assert(testStmt != nullptr && testStmt->GetNextStmt() == nullptr); - JITDUMP("\n " FMT_BB "(wt=" FMT_WT ")", curBlk->bbNum, curBlk->getBBWeight(this)); - } + Statement* newTestStmt; + if (optIsLoopTestEvalIntoTemp(testStmt, &newTestStmt)) + { + testStmt = newTestStmt; } - JITDUMP("\n"); -} - -/***************************************************************************************************** - * - * Function called to update the loop table and bbWeight before removing a block - */ + // Check if we have the incr stmt before the test stmt, if we don't, + // check if incr is part of the loop "top". + Statement* incrStmt = testStmt->GetPrevStmt(); -void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmarkLoop) -{ - if (!optLoopTableValid) + // If we've added profile instrumentation, we may need to skip past a BB counter update. + // + if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) && (incrStmt != nullptr) && + incrStmt->GetRootNode()->IsBlockProfileUpdate()) { - return; + incrStmt = incrStmt->GetPrevStmt(); } - noway_assert(!opts.MinOpts()); + if (incrStmt == nullptr || (optIsLoopIncrTree(incrStmt->GetRootNode()) == BAD_VAR_NUM)) + { + return false; + } - // If an unreachable block is a loop entry or bottom then the loop is unreachable. - // Special case: the block was the head of a loop - or pointing to a loop entry. + assert(testStmt != incrStmt); - for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) + // Find the last statement in the loop pre-header which we expect to be the initialization of + // the loop iterator. + BasicBlock* initBlock = *pInitBlock; + Statement* phdrStmt = initBlock->firstStmt(); + if (phdrStmt == nullptr) { - LoopDsc& loop = optLoopTable[loopNum]; - - // Some loops may have been already removed by loop unrolling or conditional folding. - if (loop.lpIsRemoved()) + // When we build the loops, we canonicalize by introducing loop pre-headers for all loops. + // If we are rebuilding the loops, we would already have the pre-header block introduced + // the first time, which might be empty if no hoisting has yet occurred. In this case, look a + // little harder for the possible loop initialization statement. + if (initBlock->KindIs(BBJ_ALWAYS) && initBlock->TargetIs(top) && (initBlock->countOfInEdges() == 1) && + !initBlock->IsFirst() && initBlock->Prev()->bbFallsThrough()) { - continue; + initBlock = initBlock->Prev(); + phdrStmt = initBlock->firstStmt(); } + } - // Avoid printing to the JitDump unless we're actually going to change something. - // If we call reportBefore, then we're going to change the loop table, and we should print the - // `reportAfter` info as well. Only print the `reportBefore` info once, if multiple changes to - // the table are made. - INDEBUG(bool reportedBefore = false); + if (phdrStmt != nullptr) + { + Statement* initStmt = phdrStmt->GetPrevStmt(); + noway_assert(initStmt != nullptr && (initStmt->GetNextStmt() == nullptr)); - auto reportBefore = [&]() { + // If it is a duplicated loop condition, skip it. + if (initStmt->GetRootNode()->OperIs(GT_JTRUE)) + { + bool doGetPrev = true; #ifdef DEBUG - if (verbose && !reportedBefore) + if (opts.optRepeat) { - printf("optUpdateLoopsBeforeRemoveBlock " FMT_BB " Before: ", block->bbNum); - optPrintLoopInfo(loopNum); - printf("\n"); - reportedBefore = true; + // Previous optimization passes may have inserted compiler-generated + // statements other than duplicated loop conditions. + doGetPrev = (initStmt->GetPrevStmt() != nullptr); } #endif // DEBUG - }; - - auto reportAfter = [&]() { -#ifdef DEBUG - if (verbose && reportedBefore) + if (doGetPrev) { - printf("optUpdateLoopsBeforeRemoveBlock " FMT_BB " After: ", block->bbNum); - optPrintLoopInfo(loopNum); - printf("\n"); + initStmt = initStmt->GetPrevStmt(); } -#endif // DEBUG - }; - - if ((block == loop.lpEntry) || (block == loop.lpBottom) || (block == loop.lpTop)) - { - reportBefore(); - optMarkLoopRemoved(loopNum); - reportAfter(); - continue; + noway_assert(initStmt != nullptr); } - // If the loop is still in the table any block in the loop must be reachable. - - noway_assert((loop.lpEntry != block) && (loop.lpBottom != block)); + *ppInit = initStmt->GetRootNode(); + *pInitBlock = initBlock; + } + else + { + *ppInit = nullptr; + } - if (loop.lpExit == block) - { - reportBefore(); - assert(loop.lpExitCnt == 1); - --loop.lpExitCnt; - loop.lpExit = nullptr; - } + *ppTest = testStmt->GetRootNode(); + *ppIncr = incrStmt->GetRootNode(); - // If `block` flows to the loop entry then the whole loop will become unreachable if it is the - // only non-loop predecessor. + return true; +} - bool removeLoop = false; - if (!loop.lpContains(block)) +#ifdef DEBUG +void Compiler::optCheckPreds() +{ + for (BasicBlock* const block : Blocks()) + { + for (BasicBlock* const predBlock : block->PredBlocks()) { - for (BasicBlock* const succ : block->Succs()) + // make sure this pred is part of the BB list + BasicBlock* bb; + for (bb = fgFirstBB; bb; bb = bb->Next()) { - if (loop.lpEntry == succ) + if (bb == predBlock) { - removeLoop = true; break; } } - - if (removeLoop) + noway_assert(bb); + switch (bb->GetKind()) { - // If the entry has any non-loop block that is not the known 'block' predecessor of entry - // (found above), then don't remove the loop. - for (BasicBlock* const predBlock : loop.lpEntry->PredBlocks()) - { - if (!loop.lpContains(predBlock) && (predBlock != block)) + case BBJ_COND: + if (bb->TrueTargetIs(block)) { - removeLoop = false; break; } - } - } - } - - if (removeLoop) - { - reportBefore(); - optMarkLoopRemoved(loopNum); - } - else if (loop.lpHead == block) - { - reportBefore(); - /* The loop has a new head - Just update the loop table */ - loop.lpHead = block->Prev(); - } - - reportAfter(); - } - - if (!skipUnmarkLoop && // If we want to unmark this loop... - (fgCurBBEpochSize == fgBBNumMax + 1) && // We didn't add new blocks since last renumber... - (m_reachabilitySets != nullptr) && // Given the reachability sets are computed and valid... - (fgCurBBEpochSize == fgDomBBcount + 1)) - { - // This block must reach conditionally or always - - if (block->KindIs(BBJ_ALWAYS) && // This block always reaches - block->GetTarget()->isLoopHead() && // to a loop head... - (block->GetTarget()->bbNum <= block->bbNum) && // This is a backedge... - m_reachabilitySets->CanReach(block->GetTarget(), block)) // Block's back edge target can reach block... - { - optUnmarkLoopBlocks(block->GetTarget(), block); // Unscale the blocks in such loop. - } - else if (block->KindIs(BBJ_COND) && // This block conditionally reaches - block->GetTrueTarget()->isLoopHead() && // to a loop head... - (block->GetTrueTarget()->bbNum <= block->bbNum) && // This is a backedge... - m_reachabilitySets->CanReach(block->GetTrueTarget(), block)) // Block's back edge target can reach - // block... - { - optUnmarkLoopBlocks(block->GetTrueTarget(), block); // Unscale the blocks in such loop. - } - } -} - -#ifdef DEBUG - -/***************************************************************************** - * - * Print loop info in an uniform way. - */ - -void Compiler::optPrintLoopInfo(const LoopDsc* loop, bool printVerbose /* = false */) -{ - assert(optLoopTable != nullptr); - assert((&optLoopTable[0] <= loop) && (loop < &optLoopTable[optLoopCount])); - - unsigned lnum = (unsigned)(loop - optLoopTable); - assert(lnum < optLoopCount); - assert(&optLoopTable[lnum] == loop); - - if (loop->lpIsRemoved()) - { - // If a loop has been removed, it might be dangerous to print its fields (e.g., loop unrolling - // nulls out the lpHead field). - printf(FMT_LP " REMOVED", lnum); - return; - } - - printf(FMT_LP ", from " FMT_BB " to " FMT_BB " (Head=" FMT_BB ", Entry=" FMT_BB, lnum, loop->lpTop->bbNum, - loop->lpBottom->bbNum, loop->lpHead->bbNum, loop->lpEntry->bbNum); - - if (loop->lpExitCnt == 1) - { - printf(", Exit=" FMT_BB, loop->lpExit->bbNum); - } - else - { - printf(", ExitCnt=%d", loop->lpExitCnt); - } - - if (loop->lpParent != BasicBlock::NOT_IN_LOOP) - { - printf(", parent=" FMT_LP, loop->lpParent); - } - printf(")"); - - if (printVerbose) - { - if (loop->lpChild != BasicBlock::NOT_IN_LOOP) - { - printf(", child loop = " FMT_LP, loop->lpChild); - } - if (loop->lpSibling != BasicBlock::NOT_IN_LOOP) - { - printf(", sibling loop = " FMT_LP, loop->lpSibling); - } - - // If an iterator loop print the iterator and the initialization. - if (loop->lpFlags & LPFLG_ITER) - { - printf(" [over V%02u", loop->lpIterVar()); - printf(" ("); - printf(GenTree::OpName(loop->lpIterOper())); - printf(" %d)", loop->lpIterConst()); - - if (loop->lpFlags & LPFLG_CONST_INIT) - { - printf(" from %d", loop->lpConstInit); - } - - if (loop->lpFlags & LPFLG_CONST_INIT) - { - if (loop->lpInitBlock != loop->lpHead) - { - printf(" (in " FMT_BB ")", loop->lpInitBlock->bbNum); - } - } - - // If a simple test condition print operator and the limits */ - printf(" %s", GenTree::OpName(loop->lpTestOper())); - - if (loop->lpFlags & LPFLG_CONST_LIMIT) - { - printf(" %d", loop->lpConstLimit()); - if (loop->lpFlags & LPFLG_SIMD_LIMIT) - { - printf(" (simd)"); - } - } - if (loop->lpFlags & LPFLG_VAR_LIMIT) - { - printf(" V%02u", loop->lpVarLimit()); - } - if (loop->lpFlags & LPFLG_ARRLEN_LIMIT) - { - ArrIndex* index = new (getAllocator(CMK_DebugOnly)) ArrIndex(getAllocator(CMK_DebugOnly)); - if (loop->lpArrLenLimit(this, index)) - { - printf(" "); - index->Print(); - printf(".Length"); - } - else - { - printf(" ???.Length"); - } - } - - printf("]"); - } - - // Print the flags - - if (loop->lpFlags & LPFLG_CONTAINS_CALL) - { - printf(" call"); - } - if (loop->lpFlags & LPFLG_HAS_PREHEAD) - { - printf(" prehead"); - } - if (loop->lpFlags & LPFLG_DONT_UNROLL) - { - printf(" !unroll"); - } - if (loop->lpFlags & LPFLG_ASGVARS_YES) - { - printf(" avyes"); - } - if (loop->lpFlags & LPFLG_ASGVARS_INC) - { - printf(" avinc"); - } - } -} - -void Compiler::optPrintLoopInfo(unsigned lnum, bool printVerbose /* = false */) -{ - assert(lnum < optLoopCount); - - const LoopDsc& loop = optLoopTable[lnum]; - optPrintLoopInfo(&loop, printVerbose); -} - -//------------------------------------------------------------------------ -// optPrintLoopTable: Print the loop table -// -void Compiler::optPrintLoopTable() -{ - printf("\n*************** Natural loop table\n"); - - if (optLoopCount == 0) - { - printf("No loops\n"); - } - else - { - for (unsigned loopInd = 0; loopInd < optLoopCount; loopInd++) - { - optPrintLoopInfo(loopInd, /* verbose */ true); - printf("\n"); - } - } - - printf("\n"); -} - -#endif // DEBUG - -//------------------------------------------------------------------------ -// optPopulateInitInfo: Populate loop init info in the loop table. -// We assume the iteration variable is initialized already and check appropriately. -// This only checks for the special case of a constant initialization. -// -// Arguments: -// loopInd - loop index -// initBlock - block in which the initialization lives. -// init - the tree that is supposed to initialize the loop iterator. Might be nullptr. -// iterVar - loop iteration variable. -// -// Return Value: -// "true" if a constant initializer was found. -// -// Operation: -// The 'init' tree is checked if its lhs is a local and rhs is a const. -// -bool Compiler::optPopulateInitInfo(unsigned loopInd, BasicBlock* initBlock, GenTree* init, unsigned iterVar) -{ - // Operator should be STORE_LCL_VAR - if ((init == nullptr) || !init->OperIs(GT_STORE_LCL_VAR) || (init->AsLclVar()->GetLclNum() != iterVar)) - { - return false; - } - - // Value must be constant. TODO-CQ: CLONE: Add arr length for descending loops. - GenTree* initValue = init->AsLclVar()->Data(); - if (!initValue->IsCnsIntOrI() || (initValue->TypeGet() != TYP_INT)) - { - return false; - } - - // We found an initializer in the `initBlock` block. For this to be used, we need to make sure the - // "iterVar" initialization is never skipped. That is, every pred of ENTRY other than HEAD is in the loop. - // We allow one special case: the HEAD block is an empty predecessor to ENTRY, and the initBlock is the - // only predecessor to HEAD. This handles the case where we rebuild the loop table (after inserting - // pre-headers) and we still want to find the initializer before the pre-header block. - for (BasicBlock* const predBlock : optLoopTable[loopInd].lpEntry->PredBlocks()) - { - if (!optLoopTable[loopInd].lpContains(predBlock)) - { - bool initBlockOk = (predBlock == initBlock); - if (!initBlockOk) - { - if (predBlock->KindIs(BBJ_ALWAYS) && predBlock->TargetIs(optLoopTable[loopInd].lpEntry) && - (predBlock->countOfInEdges() == 1) && (predBlock->firstStmt() == nullptr) && - !predBlock->IsFirst() && predBlock->Prev()->bbFallsThrough()) - { - initBlockOk = true; - } - } - if (!initBlockOk) - { - JITDUMP(FMT_LP ": initialization not guaranteed from " FMT_BB " through to entry block " FMT_BB - " from pred " FMT_BB "; ignore constant initializer\n", - loopInd, initBlock->bbNum, optLoopTable[loopInd].lpEntry->bbNum, predBlock->bbNum); - return false; - } - } - } - - optLoopTable[loopInd].lpFlags |= LPFLG_CONST_INIT; - optLoopTable[loopInd].lpConstInit = (int)initValue->AsIntCon()->gtIconVal; - optLoopTable[loopInd].lpInitBlock = initBlock; - - return true; -} - -//---------------------------------------------------------------------------------- -// optCheckIterInLoopTest: Check if iteration variable is used in loop test. -// -// Arguments: -// loopInd - loop index -// test - "jtrue" tree or a store of the loop iteration termination condition -// iterVar - loop iteration variable. -// -// Operation: -// The test tree is parsed to check if "iterVar" matches the lhs of the condition -// and the rhs limit is extracted from the "test" tree. The limit information is -// added to the loop table. -// -// Return Value: -// "false" if the loop table could not be populated with the loop test info or -// if the test condition doesn't involve iterVar. -// -bool Compiler::optCheckIterInLoopTest(unsigned loopInd, GenTree* test, unsigned iterVar) -{ - // Obtain the relop from the "test" tree. - GenTree* relop; - if (test->OperIs(GT_JTRUE)) - { - relop = test->gtGetOp1(); - } - else - { - assert(test->OperIs(GT_STORE_LCL_VAR)); - relop = test->AsLclVar()->Data(); - } - - noway_assert(relop->OperIsCompare()); - - GenTree* opr1 = relop->AsOp()->gtOp1; - GenTree* opr2 = relop->AsOp()->gtOp2; - - GenTree* iterOp; - GenTree* limitOp; - - // Make sure op1 or op2 is the iterVar. - if (opr1->gtOper == GT_LCL_VAR && opr1->AsLclVarCommon()->GetLclNum() == iterVar) - { - iterOp = opr1; - limitOp = opr2; - } - else if (opr2->gtOper == GT_LCL_VAR && opr2->AsLclVarCommon()->GetLclNum() == iterVar) - { - iterOp = opr2; - limitOp = opr1; - } - else - { - return false; - } - - if (iterOp->gtType != TYP_INT) - { - return false; - } - - // Mark the iterator node. - iterOp->gtFlags |= GTF_VAR_ITERATOR; - - // Check what type of limit we have - constant, variable or arr-len. - if (limitOp->gtOper == GT_CNS_INT) - { - optLoopTable[loopInd].lpFlags |= LPFLG_CONST_LIMIT; - if ((limitOp->gtFlags & GTF_ICON_SIMD_COUNT) != 0) - { - optLoopTable[loopInd].lpFlags |= LPFLG_SIMD_LIMIT; - } - } - else if (limitOp->gtOper == GT_LCL_VAR) - { - // See if limit var is a loop invariant - // - if (!optIsVarAssgLoop(loopInd, limitOp->AsLclVarCommon()->GetLclNum())) - { - optLoopTable[loopInd].lpFlags |= LPFLG_VAR_LIMIT; - } - else - { - JITDUMP("Limit var V%02u modifiable in " FMT_LP "\n", limitOp->AsLclVarCommon()->GetLclNum(), loopInd); - } - } - else if (limitOp->gtOper == GT_ARR_LENGTH) - { - // See if limit array is a loop invariant - // - GenTree* const array = limitOp->AsArrLen()->ArrRef(); - - if (array->OperIs(GT_LCL_VAR)) - { - if (!optIsVarAssgLoop(loopInd, array->AsLclVarCommon()->GetLclNum())) - { - optLoopTable[loopInd].lpFlags |= LPFLG_ARRLEN_LIMIT; - } - else - { - JITDUMP("Array limit var V%02u modifiable in " FMT_LP "\n", array->AsLclVarCommon()->GetLclNum(), - loopInd); + noway_assert(bb->FalseTargetIs(block)); + break; + case BBJ_EHFILTERRET: + case BBJ_ALWAYS: + case BBJ_EHCATCHRET: + noway_assert(bb->TargetIs(block)); + break; + default: + break; } } - else - { - JITDUMP("Array limit tree [%06u] not analyzable in " FMT_LP "\n", dspTreeID(limitOp), loopInd); - } - } - else - { - JITDUMP("Loop limit tree [%06u] not analyzable in " FMT_LP "\n", dspTreeID(limitOp), loopInd); } - - // Were we able to successfully analyze the limit? - // - const bool analyzedLimit = - (optLoopTable[loopInd].lpFlags & (LPFLG_CONST_LIMIT | LPFLG_VAR_LIMIT | LPFLG_ARRLEN_LIMIT)) != 0; - - // Save the type of the comparison between the iterator and the limit. - // - optLoopTable[loopInd].lpTestTree = relop; - - return analyzedLimit; -} - -//---------------------------------------------------------------------------------- -// optIsLoopIncrTree: Check if loop is a tree of form v = v op const. -// -// Arguments: -// incr - The incr tree to be checked. -// -// Return Value: -// iterVar local num if the iterVar is found, otherwise BAD_VAR_NUM. -// -unsigned Compiler::optIsLoopIncrTree(GenTree* incr) -{ - GenTree* incrVal; - genTreeOps updateOper; - unsigned iterVar = incr->IsLclVarUpdateTree(&incrVal, &updateOper); - if (iterVar != BAD_VAR_NUM) - { - // We have v = v op y type node. - switch (updateOper) - { - case GT_ADD: - case GT_SUB: - case GT_MUL: - case GT_RSH: - case GT_LSH: - break; - default: - return BAD_VAR_NUM; - } - - // Increment should be by a const int. - // TODO-CQ: CLONE: allow variable increments. - if ((incrVal->gtOper != GT_CNS_INT) || (incrVal->TypeGet() != TYP_INT)) - { - return BAD_VAR_NUM; - } - } - - return iterVar; -} - -//---------------------------------------------------------------------------------- -// optComputeIterInfo: Check tree is loop increment of a lcl that is loop-invariant. -// -// Arguments: -// incr - tree that increments the loop iterator. v+=1 or v=v+1. -// from, to - range of blocks that comprise the loop body -// pIterVar - see return value. -// -// Return Value: -// Returns true if iterVar "v" can be returned in "pIterVar", otherwise returns -// false. -// -// Operation: -// Check if the "incr" tree is a "v=v+1 or v+=1" type tree and make sure it is not -// otherwise modified in the loop. -// -bool Compiler::optComputeIterInfo(GenTree* incr, BasicBlock* from, BasicBlock* to, unsigned* pIterVar) -{ - const unsigned iterVar = optIsLoopIncrTree(incr); - - if (iterVar == BAD_VAR_NUM) - { - return false; - } - - // Note we can't use optIsVarAssgLoop here, as iterVar is indeed - // assigned within the loop. - // - // Bail on promoted case, otherwise we'd have to search the loop - // for both iterVar and its parent. - // - // Bail on the potentially aliased case. - // - LclVarDsc* const iterVarDsc = lvaGetDesc(iterVar); - - if (iterVarDsc->lvIsStructField) - { - JITDUMP("iterVar V%02u is a promoted field\n", iterVar); - return false; - } - - if (iterVarDsc->IsAddressExposed()) - { - JITDUMP("iterVar V%02u is address exposed\n", iterVar); - return false; - } - - if (optIsVarAssigned(from, to, incr, iterVar)) - { - JITDUMP("iterVar V%02u is assigned in loop\n", iterVar); - return false; - } - - JITDUMP("iterVar V%02u is invariant in loop (with the exception of the update in [%06u])\n", iterVar, - dspTreeID(incr)); - - *pIterVar = iterVar; - return true; -} - -//---------------------------------------------------------------------------------- -// optIsLoopTestEvalIntoTemp: -// Pattern match if the test tree is computed into a tmp -// and the "tmp" is used as jump condition for loop termination. -// -// Arguments: -// testStmt - is the JTRUE statement that is of the form: jmpTrue (Vtmp != 0) -// where Vtmp contains the actual loop test result. -// newTestStmt - contains the statement that is the actual test stmt involving -// the loop iterator. -// -// Return Value: -// Returns true if a new test tree can be obtained. -// -// Operation: -// Scan if the current stmt is a jtrue with (Vtmp != 0) as condition -// Then returns the rhs for def of Vtmp as the "test" node. -// -// Note: -// This method just retrieves what it thinks is the "test" node, -// the callers are expected to verify that "iterVar" is used in the test. -// -bool Compiler::optIsLoopTestEvalIntoTemp(Statement* testStmt, Statement** newTestStmt) -{ - GenTree* test = testStmt->GetRootNode(); - - if (test->gtOper != GT_JTRUE) - { - return false; - } - - GenTree* relop = test->gtGetOp1(); - noway_assert(relop->OperIsCompare()); - - GenTree* opr1 = relop->AsOp()->gtOp1; - GenTree* opr2 = relop->AsOp()->gtOp2; - - // Make sure we have jtrue (vtmp != 0) - if ((relop->OperGet() == GT_NE) && (opr1->OperGet() == GT_LCL_VAR) && (opr2->OperGet() == GT_CNS_INT) && - opr2->IsIntegralConst(0)) - { - // Get the previous statement to get the def (rhs) of Vtmp to see - // if the "test" is evaluated into Vtmp. - Statement* prevStmt = testStmt->GetPrevStmt(); - if (prevStmt == nullptr) - { - return false; - } - - GenTree* tree = prevStmt->GetRootNode(); - if (tree->OperIs(GT_STORE_LCL_VAR) && (tree->AsLclVar()->GetLclNum() == opr1->AsLclVar()->GetLclNum()) && - tree->AsLclVar()->Data()->OperIsCompare()) - { - *newTestStmt = prevStmt; - return true; - } - } - return false; -} - -//---------------------------------------------------------------------------------- -// optExtractInitTestIncr: -// Extract the "init", "test" and "incr" nodes of the loop. -// -// Arguments: -// pInitBlock - [IN/OUT] *pInitBlock is the loop head block on entry, and is set to the initBlock on exit, -// if `**ppInit` is non-null. -// bottom - Loop bottom block -// top - Loop top block -// ppInit - The init stmt of the loop if found. -// ppTest - The test stmt of the loop if found. -// ppIncr - The incr stmt of the loop if found. -// -// Return Value: -// The results are put in "ppInit", "ppTest" and "ppIncr" if the method -// returns true. Returns false if the information can't be extracted. -// Extracting the `init` is optional; if one is not found, *ppInit is set -// to nullptr. Return value will never be false if `init` is not found. -// -// Operation: -// Check if the "test" stmt is last stmt in the loop "bottom". Try to find the "incr" stmt. -// Check previous stmt of "test" to get the "incr" stmt. If it is not found it could be a loop of the -// below form. -// -// +-------<-----------------<-----------+ -// | | -// v | -// BBinit(head) -> BBcond(top) -> BBLoopBody(bottom) ---^ -// -// Check if the "incr" tree is present in the loop "top" node as the last stmt. -// Also check if the "test" tree is assigned to a tmp node and the tmp is used -// in the jtrue condition. -// -// Note: -// This method just retrieves what it thinks is the "test" node, -// the callers are expected to verify that "iterVar" is used in the test. -// -bool Compiler::optExtractInitTestIncr( - BasicBlock** pInitBlock, BasicBlock* bottom, BasicBlock* top, GenTree** ppInit, GenTree** ppTest, GenTree** ppIncr) -{ - assert(pInitBlock != nullptr); - assert(ppInit != nullptr); - assert(ppTest != nullptr); - assert(ppIncr != nullptr); - - // Check if last two statements in the loop body are the increment of the iterator - // and the loop termination test. - noway_assert(bottom->bbStmtList != nullptr); - Statement* testStmt = bottom->lastStmt(); - noway_assert(testStmt != nullptr && testStmt->GetNextStmt() == nullptr); - - Statement* newTestStmt; - if (optIsLoopTestEvalIntoTemp(testStmt, &newTestStmt)) - { - testStmt = newTestStmt; - } - - // Check if we have the incr stmt before the test stmt, if we don't, - // check if incr is part of the loop "top". - Statement* incrStmt = testStmt->GetPrevStmt(); - - // If we've added profile instrumentation, we may need to skip past a BB counter update. - // - if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR) && (incrStmt != nullptr) && - incrStmt->GetRootNode()->IsBlockProfileUpdate()) - { - incrStmt = incrStmt->GetPrevStmt(); - } - - if (incrStmt == nullptr || (optIsLoopIncrTree(incrStmt->GetRootNode()) == BAD_VAR_NUM)) - { - return false; - } - - assert(testStmt != incrStmt); - - // Find the last statement in the loop pre-header which we expect to be the initialization of - // the loop iterator. - BasicBlock* initBlock = *pInitBlock; - Statement* phdrStmt = initBlock->firstStmt(); - if (phdrStmt == nullptr) - { - // When we build the loop table, we canonicalize by introducing loop pre-headers for all loops. - // If we are rebuilding the loop table, we would already have the pre-header block introduced - // the first time, which might be empty if no hoisting has yet occurred. In this case, look a - // little harder for the possible loop initialization statement. - if (initBlock->KindIs(BBJ_ALWAYS) && initBlock->TargetIs(top) && (initBlock->countOfInEdges() == 1) && - !initBlock->IsFirst() && initBlock->Prev()->bbFallsThrough()) - { - initBlock = initBlock->Prev(); - phdrStmt = initBlock->firstStmt(); - } - } - - if (phdrStmt != nullptr) - { - Statement* initStmt = phdrStmt->GetPrevStmt(); - noway_assert(initStmt != nullptr && (initStmt->GetNextStmt() == nullptr)); - - // If it is a duplicated loop condition, skip it. - if (initStmt->GetRootNode()->OperIs(GT_JTRUE)) - { - bool doGetPrev = true; -#ifdef DEBUG - if (opts.optRepeat) - { - // Previous optimization passes may have inserted compiler-generated - // statements other than duplicated loop conditions. - doGetPrev = (initStmt->GetPrevStmt() != nullptr); - } -#endif // DEBUG - if (doGetPrev) - { - initStmt = initStmt->GetPrevStmt(); - } - noway_assert(initStmt != nullptr); - } - - *ppInit = initStmt->GetRootNode(); - *pInitBlock = initBlock; - } - else - { - *ppInit = nullptr; - } - - *ppTest = testStmt->GetRootNode(); - *ppIncr = incrStmt->GetRootNode(); - - return true; -} - -/***************************************************************************** - * - * Record the loop in the loop table. Return true if successful, false if - * out of entries in loop table. - */ - -bool Compiler::optRecordLoop( - BasicBlock* head, BasicBlock* top, BasicBlock* entry, BasicBlock* bottom, BasicBlock* exit, unsigned char exitCnt) -{ - if (exitCnt == 1) - { - noway_assert(exit != nullptr); - } - - // Record this loop in the table, if there's room. - - assert(optLoopCount <= BasicBlock::MAX_LOOP_NUM); - if (optLoopCount == BasicBlock::MAX_LOOP_NUM) - { -#if COUNT_LOOPS - loopOverflowThisMethod = true; -#endif - return false; - } - - // Assumed preconditions on the loop we're adding. - assert(top->bbNum <= entry->bbNum); - assert(entry->bbNum <= bottom->bbNum); - assert(head->bbNum < top->bbNum || head->bbNum > bottom->bbNum); - - unsigned char loopInd = optLoopCount; - - if (optLoopTable == nullptr) - { - assert(loopInd == 0); - optLoopTable = getAllocator(CMK_LoopOpt).allocate(BasicBlock::MAX_LOOP_NUM); - - NewLoopEpoch(); - } - else - { - // If the new loop contains any existing ones, add it in the right place. - for (unsigned char prevPlus1 = optLoopCount; prevPlus1 > 0; prevPlus1--) - { - unsigned char prev = prevPlus1 - 1; - if (optLoopTable[prev].lpContainedBy(top, bottom)) - { - loopInd = prev; - } - } - // Move up any loops if necessary. - for (unsigned j = optLoopCount; j > loopInd; j--) - { - optLoopTable[j] = optLoopTable[j - 1]; - } - } - -#ifdef DEBUG - for (unsigned i = loopInd + 1; i < optLoopCount; i++) - { - // The loop is well-formed. - assert(optLoopTable[i].lpWellFormed()); - // Check for disjoint. - if (optLoopTable[i].lpDisjoint(top, bottom)) - { - continue; - } - // Otherwise, assert complete containment (of optLoopTable[i] in new loop). - assert(optLoopTable[i].lpContainedBy(top, bottom)); - } -#endif // DEBUG - - bool loopInsertedAtEnd = (loopInd == optLoopCount); - optLoopCount++; - - optLoopTable[loopInd].lpHead = head; - optLoopTable[loopInd].lpTop = top; - optLoopTable[loopInd].lpBottom = bottom; - optLoopTable[loopInd].lpEntry = entry; - optLoopTable[loopInd].lpExit = exit; - optLoopTable[loopInd].lpExitCnt = exitCnt; - - optLoopTable[loopInd].lpParent = BasicBlock::NOT_IN_LOOP; - optLoopTable[loopInd].lpChild = BasicBlock::NOT_IN_LOOP; - optLoopTable[loopInd].lpSibling = BasicBlock::NOT_IN_LOOP; - - optLoopTable[loopInd].lpAsgVars = AllVarSetOps::UninitVal(); - - optLoopTable[loopInd].lpFlags = LPFLG_EMPTY; - - // We haven't yet recorded any side effects. - for (MemoryKind memoryKind : allMemoryKinds()) - { - optLoopTable[loopInd].lpLoopHasMemoryHavoc[memoryKind] = false; - } - optLoopTable[loopInd].lpFieldsModified = nullptr; - optLoopTable[loopInd].lpArrayElemTypesModified = nullptr; - - // - // Try to find loops that have an iterator (i.e. for-like loops) "for (init; test; incr){ ... }" - // We have the following restrictions: - // 1. The loop condition must be a simple one i.e. only one JTRUE node - // 2. There must be a loop iterator (a local var) that is - // incremented (decremented or lsh, rsh, mul) with a constant value - // 3. The iterator is incremented exactly once - // 4. The loop condition must use the iterator. - // 5. Finding a constant initializer is optional; if the initializer is not found, or is not constant, - // it is still considered a for-like loop. - // - if (bottom->KindIs(BBJ_COND)) - { - GenTree* init; - GenTree* test; - GenTree* incr; - BasicBlock* initBlock = head; - if (!optExtractInitTestIncr(&initBlock, bottom, top, &init, &test, &incr)) - { - JITDUMP(FMT_LP ": couldn't find init/test/incr; not LPFLG_ITER loop\n", loopInd); - goto DONE_LOOP; - } - - unsigned iterVar = BAD_VAR_NUM; - if (!optComputeIterInfo(incr, top, bottom, &iterVar)) - { - JITDUMP(FMT_LP ": increment expression not appropriate form, or not loop invariant; not LPFLG_ITER loop\n", - loopInd); - goto DONE_LOOP; - } - - optPopulateInitInfo(loopInd, initBlock, init, iterVar); - - // Check that the iterator is used in the loop condition. - if (!optCheckIterInLoopTest(loopInd, test, iterVar)) - { - JITDUMP(FMT_LP ": iterator V%02u fails analysis of loop condition [%06u]; not LPFLG_ITER loop\n", loopInd, - iterVar, dspTreeID(test)); - goto DONE_LOOP; - } - - // We know the loop has an iterator at this point; flag it as LPFLG_ITER. - JITDUMP(FMT_LP ": setting LPFLG_ITER\n", loopInd); - optLoopTable[loopInd].lpFlags |= LPFLG_ITER; - - // Record iterator. - optLoopTable[loopInd].lpIterTree = incr; - -#if COUNT_LOOPS - iterLoopCount++; - - // Check if a constant iteration loop. - if ((optLoopTable[loopInd].lpFlags & LPFLG_CONST_INIT) && (optLoopTable[loopInd].lpFlags & LPFLG_CONST_LIMIT)) - { - // This is a constant loop. - constIterLoopCount++; - } -#endif - } - -DONE_LOOP: - -#ifdef DEBUG - if (verbose) - { - printf("Recorded loop %s", loopInsertedAtEnd ? "" : "(extended) "); - optPrintLoopInfo(loopInd, /* verbose */ true); - printf("\n"); - } -#endif // DEBUG - - return true; -} - -#ifdef DEBUG -void Compiler::optCheckPreds() -{ - for (BasicBlock* const block : Blocks()) - { - for (BasicBlock* const predBlock : block->PredBlocks()) - { - // make sure this pred is part of the BB list - BasicBlock* bb; - for (bb = fgFirstBB; bb; bb = bb->Next()) - { - if (bb == predBlock) - { - break; - } - } - noway_assert(bb); - switch (bb->GetKind()) - { - case BBJ_COND: - if (bb->TrueTargetIs(block)) - { - break; - } - noway_assert(bb->FalseTargetIs(block)); - break; - case BBJ_EHFILTERRET: - case BBJ_ALWAYS: - case BBJ_EHCATCHRET: - noway_assert(bb->TargetIs(block)); - break; - default: - break; - } - } - } -} - -#endif // DEBUG - -namespace -{ -//------------------------------------------------------------------------ -// LoopSearch: Class that handles scanning a range of blocks to detect a loop, -// moving blocks to make the loop body contiguous, and recording the loop. -// -// We will use the following terminology: -// HEAD - the basic block that flows into the loop ENTRY block (Currently MUST be lexically before entry). -// Not part of the looping of the loop. -// TOP - the target of the backward edge from BOTTOM, and the lexically first basic block (in bbNext order) -// within this loop. -// BOTTOM - the lexically last block in the loop (i.e. the block from which we jump to the top) -// EXIT - the predecessor of loop's unique exit edge, if it has a unique exit edge; else nullptr -// ENTRY - the entry in the loop (not necessarily the TOP), but there must be only one entry -// -// We (currently) require the body of a loop to be a contiguous (in bbNext order) sequence of basic blocks. -// When the loop is identified, blocks will be moved out to make it a compact contiguous region if possible, -// and in cases where compaction is not possible, we'll subsequently treat all blocks in the lexical range -// between TOP and BOTTOM as part of the loop even if they aren't part of the SCC. -// Regarding nesting: Since a given block can only have one back-edge (we only detect loops with back-edges -// from BBJ_COND or BBJ_ALWAYS blocks), no two loops will share the same BOTTOM. Two loops may share the -// same TOP/ENTRY as reported by LoopSearch, and optCanonicalizeLoopNest will subsequently re-write -// the CFG so that no two loops share the same TOP/ENTRY anymore. -// -// | -// v -// head -// | -// | top <--+ -// | | | -// | ... | -// | | | -// | v | -// +---> entry | -// | | -// ... | -// | | -// v | -// +-- exit/tail | -// | | | -// | ... | -// | | | -// | v | -// | bottom ---+ -// | -// +------+ -// | -// v -// -class LoopSearch -{ - - // Keeping track of which blocks are in the loop requires two block sets since we may add blocks - // as we go but the BlockSet type's max ID doesn't increase to accommodate them. Define a helper - // struct to make the ensuing code more readable. - struct LoopBlockSet - { - private: - // Keep track of blocks with bbNum <= oldBlockMaxNum in a regular BlockSet, since - // it can hold all of them. - BlockSet oldBlocksInLoop; // Blocks with bbNum <= oldBlockMaxNum - - // Keep track of blocks with bbNum > oldBlockMaxNum in a separate BlockSet, but - // indexing them by (blockNum - oldBlockMaxNum); since we won't generate more than - // one new block per old block, this must be sufficient to track any new blocks. - BlockSet newBlocksInLoop; // Blocks with bbNum > oldBlockMaxNum - - Compiler* comp; - unsigned int oldBlockMaxNum; - - public: - LoopBlockSet(Compiler* comp) - : oldBlocksInLoop(BlockSetOps::UninitVal()) - , newBlocksInLoop(BlockSetOps::UninitVal()) - , comp(comp) - , oldBlockMaxNum(comp->fgBBNumMax) - { - } - - void Reset(unsigned int seedBlockNum) - { - if (BlockSetOps::MayBeUninit(oldBlocksInLoop)) - { - // Either the block sets are uninitialized (and long), so we need to initialize - // them (and allocate their backing storage), or they are short and empty, so - // assigning MakeEmpty to them is as cheap as ClearD. - oldBlocksInLoop = BlockSetOps::MakeEmpty(comp); - newBlocksInLoop = BlockSetOps::MakeEmpty(comp); - } - else - { - // We know the backing storage is already allocated, so just clear it. - BlockSetOps::ClearD(comp, oldBlocksInLoop); - BlockSetOps::ClearD(comp, newBlocksInLoop); - } - assert(seedBlockNum <= oldBlockMaxNum); - BlockSetOps::AddElemD(comp, oldBlocksInLoop, seedBlockNum); - } - - bool CanRepresent(unsigned int blockNum) - { - // We can represent old blocks up to oldBlockMaxNum, and - // new blocks up to 2 * oldBlockMaxNum. - return (blockNum <= 2 * oldBlockMaxNum); - } - - bool IsMember(unsigned int blockNum) - { - if (blockNum > oldBlockMaxNum) - { - return BlockSetOps::IsMember(comp, newBlocksInLoop, blockNum - oldBlockMaxNum); - } - else - { - return BlockSetOps::IsMember(comp, oldBlocksInLoop, blockNum); - } - } - - void Insert(unsigned int blockNum) - { - if (blockNum > oldBlockMaxNum) - { - BlockSetOps::AddElemD(comp, newBlocksInLoop, blockNum - oldBlockMaxNum); - } - else - { - BlockSetOps::AddElemD(comp, oldBlocksInLoop, blockNum); - } - } - - bool TestAndInsert(unsigned int blockNum) - { - if (blockNum > oldBlockMaxNum) - { - unsigned int shiftedNum = blockNum - oldBlockMaxNum; - if (!BlockSetOps::IsMember(comp, newBlocksInLoop, shiftedNum)) - { - BlockSetOps::AddElemD(comp, newBlocksInLoop, shiftedNum); - return false; - } - } - else - { - if (!BlockSetOps::IsMember(comp, oldBlocksInLoop, blockNum)) - { - BlockSetOps::AddElemD(comp, oldBlocksInLoop, blockNum); - return false; - } - } - return true; - } - }; - - LoopBlockSet loopBlocks; // Set of blocks identified as part of the loop - Compiler* comp; - - // See LoopSearch class comment header for a diagram relating these fields: - BasicBlock* head; // Predecessor of unique entry edge - BasicBlock* top; // Successor of back-edge from BOTTOM - BasicBlock* bottom; // Predecessor of back-edge to TOP, also lexically last in-loop block - BasicBlock* entry; // Successor of unique entry edge - - BasicBlock* lastExit; // Most recently discovered exit block - unsigned char exitCount; // Number of discovered exit edges - unsigned int oldBlockMaxNum; // Used to identify new blocks created during compaction - BlockSet bottomBlocks; // BOTTOM blocks of already-recorded loops -#ifdef DEBUG - bool forgotExit = false; // Flags a rare case where lastExit gets nulled out, for assertions -#endif - bool changedFlowGraph = false; // Signals that loop compaction has modified the flow graph - -public: - LoopSearch(Compiler* comp) - : loopBlocks(comp), comp(comp), oldBlockMaxNum(comp->fgBBNumMax), bottomBlocks(BlockSetOps::MakeEmpty(comp)) - { - // Make sure we've renumbered such that the bitsets can hold all the bits - assert(comp->fgBBNumMax <= comp->fgCurBBEpochSize); - } - - //------------------------------------------------------------------------ - // RecordLoop: Notify the Compiler that a loop has been found. - // - // Return Value: - // true - Loop successfully recorded. - // false - Compiler has run out of loop descriptors; loop not recorded. - // - bool RecordLoop() - { - // At this point we have a compact loop - record it in the loop table. - // If we found only one exit, record it in the table too - // (otherwise an exit = nullptr in the loop table means multiple exits). - - BasicBlock* onlyExit = (exitCount == 1 ? lastExit : nullptr); - if (comp->optRecordLoop(head, top, entry, bottom, onlyExit, exitCount)) - { - // Record the BOTTOM block for future reference before returning. - assert(bottom->bbNum <= oldBlockMaxNum); - BlockSetOps::AddElemD(comp, bottomBlocks, bottom->bbNum); - return true; - } - - // Unable to record this loop because the loop descriptor table overflowed. - return false; - } - - //------------------------------------------------------------------------ - // ChangedFlowGraph: Determine whether loop compaction has modified the flow graph. - // - // Return Value: - // true - The flow graph has been modified; fgUpdateChangedFlowGraph should - // be called (which is the caller's responsibility). - // false - The flow graph has not been modified by this LoopSearch. - // - bool ChangedFlowGraph() - { - return changedFlowGraph; - } - - //------------------------------------------------------------------------ - // FindLoop: Search for a loop with the given HEAD block and back-edge. - // - // Arguments: - // head - Block to be the HEAD of any loop identified - // top - Block to be the TOP of any loop identified - // bottom - Block to be the BOTTOM of any loop identified - // - // Return Value: - // true - Found a valid loop. - // false - Did not find a valid loop. - // - // Notes: - // May modify flow graph to make loop compact before returning. - // Will set instance fields to track loop's extent and exits if a valid - // loop is found, and potentially trash them otherwise. - // - bool FindLoop(BasicBlock* head, BasicBlock* top, BasicBlock* bottom) - { - // Is this a loop candidate? - We look for "back edges", i.e. an edge from BOTTOM - // to TOP (note that this is an abuse of notation since this is not necessarily a back edge - // as the definition says, but merely an indication that we have a loop there). - // Thus, we have to be very careful and after entry discovery check that it is indeed - // the only place we enter the loop (especially for non-reducible flow graphs). - - JITDUMP("FindLoop: checking head:" FMT_BB " top:" FMT_BB " bottom:" FMT_BB "\n", head->bbNum, top->bbNum, - bottom->bbNum); - - if (top->bbNum > bottom->bbNum) // is this a backward edge? (from BOTTOM to TOP) - { - // Edge from BOTTOM to TOP is not a backward edge - JITDUMP(" " FMT_BB "->" FMT_BB " is not a backedge\n", bottom->bbNum, top->bbNum); - return false; - } - - if (bottom->bbNum > oldBlockMaxNum) - { - // Not a true back-edge; bottom is a block added to reconnect fall-through during - // loop processing, so its block number does not reflect its position. - JITDUMP(" " FMT_BB "->" FMT_BB " is not a true backedge\n", bottom->bbNum, top->bbNum); - return false; - } - - if (bottom->KindIs(BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, BBJ_CALLFINALLY, - BBJ_SWITCH)) - { - JITDUMP(" bottom odd jump kind\n"); - // BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a - // loop. - // BBJ_SWITCH that has a backward jump appears only for labeled break. - return false; - } - - // The presence of a "back edge" is an indication that a loop might be present here. - // - // Definition: A loop is: - // 1. A collection of STRONGLY CONNECTED nodes i.e. there is a path from any - // node in the loop to any other node in the loop (wholly within the loop) - // 2. The loop has a unique ENTRY, i.e. there is only one way to reach a node - // in the loop from outside the loop, and that is through the ENTRY - - // Let's find the loop ENTRY - BasicBlock* entry = FindEntry(head, top, bottom); - - if (entry == nullptr) - { - // For now, we only recognize loops where HEAD has some successor ENTRY in the loop. - JITDUMP(" can't find entry\n"); - return false; - } - - // Passed the basic checks; initialize instance state for this back-edge. - this->head = head; - this->top = top; - this->entry = entry; - this->bottom = bottom; - this->lastExit = nullptr; - this->exitCount = 0; - - if (!HasSingleEntryCycle()) - { - // There isn't actually a loop between TOP and BOTTOM - JITDUMP(" not single entry cycle\n"); - return false; - } - - if (!loopBlocks.IsMember(top->bbNum)) - { - // The "back-edge" we identified isn't actually part of the flow cycle containing ENTRY - JITDUMP(" top not in loop\n"); - return false; - } - - // Disqualify loops where the first block of the loop is less nested in EH than - // the bottom block. That is, we don't want to handle loops where the back edge - // goes from within an EH region to a first block that is outside that same EH - // region. Note that we *do* handle loops where the first block is the *first* - // block of a more nested EH region (since it is legal to branch to the first - // block of an immediately more nested EH region). So, for example, disqualify - // this: - // - // BB02 - // ... - // try { - // ... - // BB10 BBJ_COND => BB02 - // ... - // } - // - // Here, BB10 is more nested than BB02. - - if (bottom->hasTryIndex() && !comp->bbInTryRegions(bottom->getTryIndex(), top)) - { - JITDUMP("Loop 'top' " FMT_BB " is in an outer EH region compared to loop 'bottom' " FMT_BB ". Rejecting " - "loop.\n", - top->bbNum, bottom->bbNum); - return false; - } - - // We have a valid loop. - return true; - } - - //------------------------------------------------------------------------ - // GetExitCount: Return the exit count computed for the loop - // - unsigned char GetExitCount() const - { - return exitCount; - } - -private: - //------------------------------------------------------------------------ - // FindEntry: See if given HEAD flows to valid ENTRY between given TOP and BOTTOM - // - // Arguments: - // head - Block to be the HEAD of any loop identified - // top - Block to be the TOP of any loop identified - // bottom - Block to be the BOTTOM of any loop identified - // - // Return Value: - // Block to be the ENTRY of any loop identified, or nullptr if no - // such entry meeting our criteria can be found. - // - // Notes: - // Returns main entry if one is found, does not check for side-entries. - // - BasicBlock* FindEntry(BasicBlock* head, BasicBlock* top, BasicBlock* bottom) - { - if (head->KindIs(BBJ_ALWAYS)) - { - if (head->GetTarget()->bbNum <= bottom->bbNum && head->GetTarget()->bbNum >= top->bbNum) - { - // OK - we enter somewhere within the loop. - return head->GetTarget(); - } - else - { - // special case - don't consider now - // assert (!"Loop entered in weird way!"); - return nullptr; - } - } - // Can we fall through into the loop? - else if (head->KindIs(BBJ_COND)) - { - // The ENTRY is at the TOP (a do-while loop) - return top; - } - else - { - return nullptr; // HEAD does not flow into the loop; bail for now - } - } - - //------------------------------------------------------------------------ - // HasSingleEntryCycle: Perform a reverse flow walk from ENTRY, visiting - // only blocks between TOP and BOTTOM, to determine if such a cycle - // exists and if it has a single entry. - // - // Return Value: - // true - Found a single-entry cycle. - // false - Did not find a single-entry cycle. - // - // Notes: - // Will mark (in `loopBlocks`) all blocks found to participate in the cycle. - // - bool HasSingleEntryCycle() - { - // Now do a backwards flow walk from entry to see if we have a single-entry loop - bool foundCycle = false; - - // Seed the loop block set and worklist with the entry block. - loopBlocks.Reset(entry->bbNum); - jitstd::list worklist(comp->getAllocator(CMK_LoopOpt)); - worklist.push_back(entry); - - while (!worklist.empty()) - { - BasicBlock* block = worklist.back(); - worklist.pop_back(); - - // Make sure ENTRY dominates all blocks in the loop. - if (block->bbNum > oldBlockMaxNum) - { - // This is a new block we added to connect fall-through, so the - // recorded dominator information doesn't cover it. Just continue, - // and when we process its unique predecessor we'll abort if ENTRY - // doesn't dominate that. - } - else if (!comp->m_domTree->Dominates(entry, block)) - { - JITDUMP(" (find cycle) entry:" FMT_BB " does not dominate " FMT_BB "\n", entry->bbNum, block->bbNum); - return false; - } - - // Add preds to the worklist, checking for side-entries. - for (BasicBlock* const predBlock : block->PredBlocks()) - { - unsigned int testNum = PositionNum(predBlock); - - if ((testNum < top->bbNum) || (testNum > bottom->bbNum)) - { - // Pred is out of loop range - if (block == entry) - { - if (predBlock == head) - { - // This is the single entry we expect. - continue; - } - // ENTRY has some pred other than head outside the loop. If ENTRY does not - // dominate this pred, we'll consider this a side-entry and skip this loop; - // otherwise the loop is still valid and this may be a (flow-wise) back-edge - // of an outer loop. For the dominance test, if `predBlock` is a new block, use - // its unique predecessor since the dominator tree has info for that. - BasicBlock* effectivePred = (predBlock->bbNum > oldBlockMaxNum ? predBlock->Prev() : predBlock); - if (comp->m_domTree->Dominates(entry, effectivePred)) - { - // Outer loop back-edge - continue; - } - } - - // There are multiple entries to this loop, don't consider it. - - JITDUMP(" (find cycle) multiple entry:" FMT_BB "\n", block->bbNum); - return false; - } - - bool isFirstVisit; - if (predBlock == entry) - { - // We have indeed found a cycle in the flow graph. - JITDUMP(" (find cycle) found cycle\n"); - isFirstVisit = !foundCycle; - foundCycle = true; - assert(loopBlocks.IsMember(predBlock->bbNum)); - } - else if (loopBlocks.TestAndInsert(predBlock->bbNum)) - { - // Already visited this pred - isFirstVisit = false; - } - else - { - // Add this predBlock to the worklist - worklist.push_back(predBlock); - isFirstVisit = true; - } - - if (isFirstVisit && !predBlock->IsLast() && (PositionNum(predBlock->Next()) == predBlock->bbNum)) - { - // We've created a new block immediately after `predBlock` to - // reconnect what was fall-through. Mark it as in-loop also; - // it needs to stay with `prev` and if it exits the loop we'd - // just need to re-create it if we tried to move it out. - loopBlocks.Insert(predBlock->Next()->bbNum); - } - } - } - - return foundCycle; - } - - //------------------------------------------------------------------------ - // PositionNum: Get the number identifying a block's position per the - // lexical ordering that existed before searching for (and compacting) - // loops. - // - // Arguments: - // block - Block whose position is desired. - // - // Return Value: - // A number indicating that block's position relative to others. - // - // Notes: - // When the given block is a new one created during loop compaction, - // the number of its unique predecessor is returned. - // - unsigned int PositionNum(BasicBlock* block) - { - if (block->bbNum > oldBlockMaxNum) - { - // This must be a block we inserted to connect fall-through after moving blocks. - // To determine if it's in the loop or not, use the number of its unique predecessor - // block. - assert(block->PrevIs(block->bbPreds->getSourceBlock())); - assert(block->bbPreds->getNextPredEdge() == nullptr); - return block->Prev()->bbNum; - } - return block->bbNum; - } -}; -} // end (anonymous) namespace - -//------------------------------------------------------------------------ -// optFindNaturalLoops: Find the natural loops, using dominators. Note that the test for -// a loop is slightly different from the standard one, because we have not done a depth -// first reordering of the basic blocks. -// -// See LoopSearch class comment header for a description of the loops found. -// -// We will find and record a maximum of BasicBlock::MAX_LOOP_NUM loops (currently 64). -// -void Compiler::optFindNaturalLoops() -{ -#ifdef DEBUG - if (verbose) - { - printf("*************** In optFindNaturalLoops()\n"); - } -#endif // DEBUG - - assert(fgHasLoops); - -#if COUNT_LOOPS - hasMethodLoops = false; - loopsThisMethod = 0; - loopOverflowThisMethod = false; -#endif - - LoopSearch search(this); - - // TODO-Quirk: Remove - BasicBlock* first = fgCanonicalizedFirstBB ? fgFirstBB->Next() : fgFirstBB; - - for (BasicBlock* head = first; !head->IsLast(); head = head->Next()) - { - BasicBlock* top = head->Next(); - - // Blocks that are rarely run have a zero bbWeight and should never be optimized here. - if (top->bbWeight == BB_ZERO_WEIGHT) - { - continue; - } - - for (BasicBlock* const predBlock : top->PredBlocks()) - { - if (search.FindLoop(head, top, predBlock)) - { - // Found a loop; record it and see if we've hit the limit. - bool recordedLoop = search.RecordLoop(); - - (void)recordedLoop; // avoid unusued variable warnings in COUNT_LOOPS and !DEBUG - -#if COUNT_LOOPS - if (!hasMethodLoops) - { - // Mark the method as containing natural loops - totalLoopMethods++; - hasMethodLoops = true; - } - - // Increment total number of loops found - totalLoopCount++; - loopsThisMethod++; - - // Keep track of the number of exits - loopExitCountTable.record(static_cast(search.GetExitCount())); - - // Note that we continue to look for loops even if - // (optLoopCount == BasicBlock::MAX_LOOP_NUM), in contrast to the !COUNT_LOOPS code below. - // This gives us a better count and stats. Hopefully it doesn't affect actual codegen. - CLANG_FORMAT_COMMENT_ANCHOR; - -#else // COUNT_LOOPS - assert(recordedLoop); - if (optLoopCount == BasicBlock::MAX_LOOP_NUM) - { - // We won't be able to record any more loops, so stop looking. - goto NO_MORE_LOOPS; - } -#endif // COUNT_LOOPS - - // Continue searching preds of `top` to see if any other are - // back-edges (this can happen for nested loops). The iteration - // is safe because the compaction we do only modifies predecessor - // lists of blocks that gain or lose fall-through from their - // `bbPrev`, but since the motion is from within the loop to below - // it, we know we're not altering the relationship between `top` - // and its `bbPrev`. - } - } - } - -#if !COUNT_LOOPS -NO_MORE_LOOPS: -#endif // !COUNT_LOOPS - -#if COUNT_LOOPS - loopCountTable.record(loopsThisMethod); - if (maxLoopsPerMethod < loopsThisMethod) - { - maxLoopsPerMethod = loopsThisMethod; - } - if (loopOverflowThisMethod) - { - totalLoopOverflows++; - } -#endif // COUNT_LOOPS - - bool mod = search.ChangedFlowGraph(); - - if (mod) - { - fgInvalidateDfsTree(); - fgRenumberBlocks(); - m_dfsTree = fgComputeDfs(); - m_domTree = FlowGraphDominatorTree::Build(m_dfsTree); - } - - // Now the loop indices are stable. We can figure out parent/child relationships - // (using table indices to name loops), and label blocks. - for (unsigned char loopInd = 1; loopInd < optLoopCount; loopInd++) - { - for (unsigned char possibleParent = loopInd; possibleParent > 0;) - { - possibleParent--; - if (optLoopTable[possibleParent].lpContains(optLoopTable[loopInd])) - { - optLoopTable[loopInd].lpParent = possibleParent; - optLoopTable[loopInd].lpSibling = optLoopTable[possibleParent].lpChild; - optLoopTable[possibleParent].lpChild = loopInd; - break; - } - } - } - - // Now label the blocks with the innermost loop to which they belong. Since parents - // precede children in the table, doing the labeling for each loop in order will achieve - // this -- the innermost loop labeling will be done last. (Inner loop blocks will be - // labeled multiple times before being correct at the end.) - for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++) - { - for (BasicBlock* const blk : optLoopTable[loopInd].LoopBlocks()) - { - blk->bbNatLoopNum = loopInd; - } - } - - for (BasicBlock* block : Blocks()) - { - block->RemoveFlags(BBF_OLD_LOOP_HEADER_QUIRK); - } - - for (unsigned loopInd = 0; loopInd < optLoopCount; loopInd++) - { - optLoopTable[loopInd].lpEntry->SetFlags(BBF_OLD_LOOP_HEADER_QUIRK); - } - -#ifdef DEBUG - if (verbose && (optLoopCount > 0)) - { - optPrintLoopTable(); - } -#endif // DEBUG } +#endif // DEBUG + //------------------------------------------------------------------------ // optRedirectBlock: Replace the branch successors of a block based on a block map. // @@ -2255,97 +707,6 @@ void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, R } } -// Returns true if 'block' is an entry block for any loop in 'optLoopTable' -bool Compiler::optIsLoopEntry(BasicBlock* block) const -{ - for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++) - { - if (optLoopTable[loopInd].lpIsRemoved()) - { - continue; - } - - if (optLoopTable[loopInd].lpEntry == block) - { - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- -// optLoopContains: Check if one loop contains another -// -// Arguments: -// l1 -- loop num of containing loop (must be valid loop num) -// l2 -- loop num of contained loop (valid loop num, or NOT_IN_LOOP) -// -// Returns: -// True if loop described by l2 is contained within l1. -// -// Notes: -// A loop contains itself. -// -bool Compiler::optLoopContains(unsigned l1, unsigned l2) const -{ - assert(l1 < optLoopCount); - assert((l2 < optLoopCount) || (l2 == BasicBlock::NOT_IN_LOOP)); - - if (l1 == l2) - { - return true; - } - else if (l2 == BasicBlock::NOT_IN_LOOP) - { - return false; - } - else - { - return optLoopContains(l1, optLoopTable[l2].lpParent); - } -} - -//----------------------------------------------------------------------------- -// optLoopEntry: For a given preheader of a loop, returns the lpEntry. -// -// Arguments: -// preHeader -- preheader of a loop -// -// Returns: -// Corresponding loop entry block. -// -BasicBlock* Compiler::optLoopEntry(BasicBlock* preHeader) -{ - assert(preHeader->HasFlag(BBF_LOOP_PREHEADER)); - assert(preHeader->KindIs(BBJ_ALWAYS)); - return preHeader->GetTarget(); -} - -//----------------------------------------------------------------------------- -// optUpdateLoopHead: Replace the `head` block of a loop in the loop table. -// Considers all child loops that might share the same head (recursively). -// -// Arguments: -// loopInd -- loop num of loop -// from -- current loop head block -// to -- replacement loop head block -// -void Compiler::optUpdateLoopHead(unsigned loopInd, BasicBlock* from, BasicBlock* to) -{ - assert(optLoopTable[loopInd].lpHead == from); - JITDUMP("Replace " FMT_LP " head " FMT_BB " with " FMT_BB "\n", loopInd, from->bbNum, to->bbNum); - optLoopTable[loopInd].lpHead = to; - for (unsigned char childLoop = optLoopTable[loopInd].lpChild; // - childLoop != BasicBlock::NOT_IN_LOOP; // - childLoop = optLoopTable[childLoop].lpSibling) - { - if (optLoopTable[childLoop].lpHead == from) - { - optUpdateLoopHead(childLoop, from, to); - } - } -} - //----------------------------------------------------------------------------- // optIterSmallOverflow: Helper for loop unrolling. Determine if "i += const" will // cause an overflow exception for the small types. @@ -4143,8 +2504,8 @@ void Compiler::optMarkLoopHeads() } //----------------------------------------------------------------------------- -// optResetLoopInfo: reset all loop info in preparation for rebuilding the loop table, or preventing -// future phases from accessing loop-related data. +// optResetLoopInfo: reset all loop info in preparation for refinding the loops +// and scaling blocks based on it. // void Compiler::optResetLoopInfo() { @@ -4155,16 +2516,6 @@ void Compiler::optResetLoopInfo() } #endif - optLoopCount = 0; // This will force the table to be rebuilt - - // This will cause users to crash if they use the table when it is considered empty. - // TODO: the loop table is always allocated as the same (maximum) size, so this is wasteful. - // We could zero it out (possibly only in DEBUG) to be paranoid, but there's no reason to - // force it to be re-allocated. - optLoopTable = nullptr; - optLoopTableValid = false; - optLoopsRequirePreHeaders = false; - for (BasicBlock* const block : Blocks()) { // If the block weight didn't come from profile data, reset it so it can be calculated again. @@ -4175,7 +2526,6 @@ void Compiler::optResetLoopInfo() } block->RemoveFlags(BBF_LOOP_FLAGS); - block->bbNatLoopNum = BasicBlock::NOT_IN_LOOP; } } @@ -4283,7 +2633,7 @@ void Compiler::optFindAndScaleGeneralLoopBlocks() // optFindLoops: find loops in the function. // // The JIT recognizes two types of loops in a function: natural loops and "general" (or "unnatural") loops. -// Natural loops are those which get added to the loop table. Most downstream optimizations require +// Natural loops are those which get added to Compiler::m_loops. Most downstream optimizations require // using natural loops. See `FlowGraphNaturalLoop` for a definition of the criteria satisfied by a natural loop. // A general loop is defined as a lexical (program order) range of blocks where a later block branches to an // earlier block (that is, there is a back edge in the flow graph), and the later block is reachable from the earlier @@ -4292,56 +2642,34 @@ void Compiler::optFindAndScaleGeneralLoopBlocks() // Notes: // Also (re)sets all non-IBC block weights. // -void Compiler::optFindLoops() +PhaseStatus Compiler::optFindLoopsPhase() { #ifdef DEBUG if (verbose) { - printf("*************** In optFindLoops()\n"); + printf("*************** In optFindLoopsPhase()\n"); } #endif - noway_assert(opts.OptimizationEnabled()); - optMarkLoopHeads(); - // Were there any potential loops in the flow graph? - - if (fgHasLoops) - { - optFindNaturalLoops(); - } - - optLoopTableValid = true; -} - -//----------------------------------------------------------------------------- -// optFindLoopsPhase: The wrapper function for the "find loops" phase. -// -PhaseStatus Compiler::optFindLoopsPhase() -{ + assert(m_dfsTree != nullptr); optFindLoops(); - m_dfsTree = fgComputeDfs(); - optFindNewLoops(); - if (fgHasLoops) { optFindAndScaleGeneralLoopBlocks(); } - // The old loop table is no longer valid. - optLoopTableValid = false; - optLoopTable = nullptr; - optLoopCount = 0; + optNumNaturalLoopsFound = (unsigned)m_loops->NumLoops(); return PhaseStatus::MODIFIED_EVERYTHING; } //----------------------------------------------------------------------------- -// optFindNewLoops: Compute new loops and cross validate with old loop table. +// optFindLoops: Find, compact and canonicalize natural loops. // -void Compiler::optFindNewLoops() +void Compiler::optFindLoops() { m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); @@ -4898,54 +3226,6 @@ void Compiler::optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* pre } } -/***************************************************************************** - * - * Determine the kind of interference for the call. - */ - -/* static */ inline Compiler::callInterf Compiler::optCallInterf(GenTreeCall* call) -{ - // if not a helper, kills everything - if (call->gtCallType != CT_HELPER) - { - return CALLINT_ALL; - } - - // setfield and array address store kill all indirections - switch (eeGetHelperNum(call->gtCallMethHnd)) - { - case CORINFO_HELP_ASSIGN_REF: // Not strictly needed as we don't make a GT_CALL with this - case CORINFO_HELP_CHECKED_ASSIGN_REF: // Not strictly needed as we don't make a GT_CALL with this - case CORINFO_HELP_ASSIGN_BYREF: // Not strictly needed as we don't make a GT_CALL with this - case CORINFO_HELP_SETFIELDOBJ: - case CORINFO_HELP_ARRADDR_ST: - - return CALLINT_REF_INDIRS; - - case CORINFO_HELP_SETFIELDFLOAT: - case CORINFO_HELP_SETFIELDDOUBLE: - case CORINFO_HELP_SETFIELD8: - case CORINFO_HELP_SETFIELD16: - case CORINFO_HELP_SETFIELD32: - case CORINFO_HELP_SETFIELD64: - - return CALLINT_SCL_INDIRS; - - case CORINFO_HELP_ASSIGN_STRUCT: // Not strictly needed as we don't use this - case CORINFO_HELP_MEMSET: // Not strictly needed as we don't make a GT_CALL with this - case CORINFO_HELP_MEMCPY: // Not strictly needed as we don't make a GT_CALL with this - case CORINFO_HELP_SETFIELDSTRUCT: - - return CALLINT_ALL_INDIRS; - - default: - break; - } - - // other helpers kill nothing - return CALLINT_NONE; -} - /***************************************************************************** * * See if the given tree can be computed in the given precision (which must @@ -5219,390 +3499,96 @@ bool Compiler::optNarrowTree(GenTree* tree, var_types srct, var_types dstt, Valu { if (tree->gtOper == GT_MUL && (tree->gtFlags & GTF_MUL_64RSLT)) { - tree->gtFlags &= ~GTF_MUL_64RSLT; - } - - tree->gtType = genActualType(dstt); - tree->SetVNs(vnpNarrow); - } - - return true; - - case GT_IND: - - NARROW_IND: - - if ((dstSize > genTypeSize(tree->gtType)) && - (varTypeIsUnsigned(dstt) && !varTypeIsUnsigned(tree->gtType))) - { - return false; - } - - /* Simply change the type of the tree */ - - if (doit && (dstSize <= genTypeSize(tree->gtType))) - { - if (!varTypeIsSmall(dstt)) - { - dstt = varTypeToSigned(dstt); - } - - tree->gtType = dstt; - tree->SetVNs(vnpNarrow); - } - - return true; - - case GT_EQ: - case GT_NE: - case GT_LT: - case GT_LE: - case GT_GT: - case GT_GE: - - /* These can always be narrowed since they only represent 0 or 1 */ - return true; - - case GT_CAST: - { -#ifdef DEBUG - if ((tree->gtDebugFlags & GTF_DEBUG_CAST_DONT_FOLD) != 0) - { - return false; - } -#endif - - if ((tree->CastToType() != srct) || tree->gtOverflow()) - { - return false; - } - - if (varTypeIsInt(op1) && varTypeIsInt(dstt) && tree->TypeIs(TYP_LONG)) - { - // We have a CAST that converts into to long while dstt is int. - // so we can just convert the cast to int -> int and someone will clean it up. - if (doit) - { - tree->CastToType() = TYP_INT; - tree->ChangeType(TYP_INT); - tree->ClearUnsigned(); - } - return true; - } - } - return false; - - case GT_COMMA: - if (!gtIsActiveCSE_Candidate(op2) && optNarrowTree(op2, srct, dstt, vnpNarrow, doit)) - { - /* Simply change the type of the tree */ - - if (doit) - { - tree->gtType = genActualType(dstt); - tree->SetVNs(vnpNarrow); - } - return true; - } - return false; - - default: - noway_assert(doit == false); - return false; - } - } - - return false; -} - -//------------------------------------------------------------------------ -// optIsVarAssignedWithDesc: do a walk to record local modification data for a statement -// -// Arguments: -// stmt - the statement to walk -// dsc - [in, out] data for the walk -// -bool Compiler::optIsVarAssignedWithDesc(Statement* stmt, isVarAssgDsc* dsc) -{ - class IsVarAssignedVisitor : public GenTreeVisitor - { - isVarAssgDsc* m_dsc; - - public: - enum - { - DoPreOrder = true, - UseExecutionOrder = true, - }; - - IsVarAssignedVisitor(Compiler* comp, isVarAssgDsc* dsc) : GenTreeVisitor(comp), m_dsc(dsc) - { - } - - fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) - { - GenTree* const tree = *use; - - if (tree->OperIs(GT_STOREIND)) - { - // Set the proper indirection bits. - varRefKinds refs = varTypeIsGC(tree) ? VR_IND_REF : VR_IND_SCL; - m_dsc->ivaMaskInd = varRefKinds(m_dsc->ivaMaskInd | refs); - } - - // Can this tree define a local? - // - if (!tree->OperIsSsaDef()) - { - return WALK_CONTINUE; - } - - // Determine what's written and check for calls. - // - if (tree->OperIs(GT_CALL)) - { - m_dsc->ivaMaskCall = optCallInterf(tree->AsCall()); - } - else if (tree->OperIs(GT_STORE_LCL_FLD)) - { - // We can't track every field of every var. Moreover, indirections - // may access different parts of the var as different (but overlapping) - // fields. So just treat them as indirect accesses. - varRefKinds refs = varTypeIsGC(tree->TypeGet()) ? VR_IND_REF : VR_IND_SCL; - m_dsc->ivaMaskInd = varRefKinds(m_dsc->ivaMaskInd | refs); - } - - // Determine if the tree modifies a particular local - // - GenTreeLclVarCommon* lcl = nullptr; - if (tree->DefinesLocal(m_compiler, &lcl)) - { - const unsigned lclNum = lcl->GetLclNum(); - - if (lclNum < lclMAX_ALLSET_TRACKED) - { - AllVarSetOps::AddElemD(m_compiler, m_dsc->ivaMaskVal, lclNum); - } - else - { - m_dsc->ivaMaskIncomplete = true; - } - - // Bail out if we were checking for one particular local - // and we now see it's modified (ignoring perhaps - // the one tree where we expect modifications). - // - if ((lclNum == m_dsc->ivaVar) && (tree != m_dsc->ivaSkip)) - { - return WALK_ABORT; - } - } - - return WALK_CONTINUE; - } - }; - - IsVarAssignedVisitor walker(this, dsc); - return walker.WalkTree(stmt->GetRootNodePointer(), nullptr) != WALK_CONTINUE; -} - -//------------------------------------------------------------------------ -// optIsVarAssigned: see if a local is assigned in a range of blocks -// -// Arguments: -// beg - first block in range -// end - last block in range -// skip - tree to ignore (nullptr if none) -// var - local to check -// -// Returns: -// true if local is directly modified -// -// Notes: -// Does a full walk of all blocks/statements/trees, so potentially expensive. -// -// Does not do proper checks for struct fields or exposed locals. -// -bool Compiler::optIsVarAssigned(BasicBlock* beg, BasicBlock* end, GenTree* skip, unsigned var) -{ - isVarAssgDsc desc; - - desc.ivaSkip = skip; - desc.ivaVar = var; - desc.ivaMaskCall = CALLINT_NONE; - AllVarSetOps::AssignNoCopy(this, desc.ivaMaskVal, AllVarSetOps::MakeEmpty(this)); + tree->gtFlags &= ~GTF_MUL_64RSLT; + } - for (;;) - { - noway_assert(beg != nullptr); + tree->gtType = genActualType(dstt); + tree->SetVNs(vnpNarrow); + } - for (Statement* const stmt : beg->Statements()) - { - if (optIsVarAssignedWithDesc(stmt, &desc)) - { return true; - } - } - - if (beg == end) - { - break; - } - beg = beg->Next(); - } + case GT_IND: - return false; -} + NARROW_IND: -//------------------------------------------------------------------------ -// optIsVarAssgLoop: see if a local is assigned in a loop -// -// Arguments: -// lnum - loop number -// var - var to check -// -// Returns: -// true if var can possibly be modified in the loop specified by lnum -// false if var is a loop invariant -// -bool Compiler::optIsVarAssgLoop(unsigned lnum, unsigned var) -{ - assert(lnum < optLoopCount); + if ((dstSize > genTypeSize(tree->gtType)) && + (varTypeIsUnsigned(dstt) && !varTypeIsUnsigned(tree->gtType))) + { + return false; + } - LclVarDsc* const varDsc = lvaGetDesc(var); - if (varDsc->IsAddressExposed()) - { - // Assume the worst (that var is possibly modified in the loop) - // - return true; - } + /* Simply change the type of the tree */ - if (var < lclMAX_ALLSET_TRACKED) - { - ALLVARSET_TP vs(AllVarSetOps::MakeSingleton(this, var)); + if (doit && (dstSize <= genTypeSize(tree->gtType))) + { + if (!varTypeIsSmall(dstt)) + { + dstt = varTypeToSigned(dstt); + } - // If local is a promoted field, also check for modifications to parent. - // - if (varDsc->lvIsStructField) - { - unsigned const parentVar = varDsc->lvParentLcl; - assert(!lvaGetDesc(parentVar)->IsAddressExposed()); - assert(lvaGetDesc(parentVar)->lvPromoted); + tree->gtType = dstt; + tree->SetVNs(vnpNarrow); + } - if (parentVar < lclMAX_ALLSET_TRACKED) - { - JITDUMP("optIsVarAssgLoop: V%02u promoted, also checking V%02u\n", var, parentVar); - AllVarSetOps::AddElemD(this, vs, parentVar); - } - else - { - // Parent var index is too large, assume the worst. - // return true; - } - } - - return optIsSetAssgLoop(lnum, vs) != 0; - } - else - { - if (varDsc->lvIsStructField) - { - return true; - } - - return optIsVarAssigned(optLoopTable[lnum].lpHead->Next(), optLoopTable[lnum].lpBottom, nullptr, var); - } -} -//------------------------------------------------------------------------ -// optIsSetAssgLoop: see if a set of locals is assigned in a loop -// -// Arguments: -// lnum - loop number -// vars - var set to check -// inds - also consider impact of indirect stores and calls -// -// Returns: -// true if any of vars are possibly modified in any of the blocks of the -// loop specified by lnum, or if the loop contains any of the specified -// aliasing operations. -// -// Notes: -// Uses a cache to avoid repeatedly scanning the loop blocks. However this -// cache never invalidates and so this method must be used with care. -// -bool Compiler::optIsSetAssgLoop(unsigned lnum, ALLVARSET_VALARG_TP vars, varRefKinds inds) -{ - noway_assert(lnum < optLoopCount); - LoopDsc* loop = &optLoopTable[lnum]; + case GT_EQ: + case GT_NE: + case GT_LT: + case GT_LE: + case GT_GT: + case GT_GE: - // Do we already know what variables are assigned within this loop? - // - if (!(loop->lpFlags & LPFLG_ASGVARS_YES)) - { - isVarAssgDsc desc; + /* These can always be narrowed since they only represent 0 or 1 */ + return true; - // Prepare the descriptor used by the tree walker call-back - // - desc.ivaVar = (unsigned)-1; - desc.ivaSkip = nullptr; - AllVarSetOps::AssignNoCopy(this, desc.ivaMaskVal, AllVarSetOps::MakeEmpty(this)); - desc.ivaMaskInd = VR_NONE; - desc.ivaMaskCall = CALLINT_NONE; - desc.ivaMaskIncomplete = false; - - // Now walk all the statements of the loop - // - for (BasicBlock* const block : loop->LoopBlocks()) - { - for (Statement* const stmt : block->NonPhiStatements()) + case GT_CAST: { - optIsVarAssignedWithDesc(stmt, &desc); - - if (desc.ivaMaskIncomplete) +#ifdef DEBUG + if ((tree->gtDebugFlags & GTF_DEBUG_CAST_DONT_FOLD) != 0) { - loop->lpFlags |= LPFLG_ASGVARS_INC; + return false; } - } - } +#endif - AllVarSetOps::Assign(this, loop->lpAsgVars, desc.ivaMaskVal); - loop->lpAsgInds = desc.ivaMaskInd; - loop->lpAsgCall = desc.ivaMaskCall; + if ((tree->CastToType() != srct) || tree->gtOverflow()) + { + return false; + } - // Now we know what variables are assigned in the loop - // - loop->lpFlags |= LPFLG_ASGVARS_YES; - } + if (varTypeIsInt(op1) && varTypeIsInt(dstt) && tree->TypeIs(TYP_LONG)) + { + // We have a CAST that converts into to long while dstt is int. + // so we can just convert the cast to int -> int and someone will clean it up. + if (doit) + { + tree->CastToType() = TYP_INT; + tree->ChangeType(TYP_INT); + tree->ClearUnsigned(); + } + return true; + } + } + return false; - // Now we can finally test the caller's mask against the loop's - // - if (!AllVarSetOps::IsEmptyIntersection(this, loop->lpAsgVars, vars) || (loop->lpAsgInds & inds)) - { - return true; - } + case GT_COMMA: + if (!gtIsActiveCSE_Candidate(op2) && optNarrowTree(op2, srct, dstt, vnpNarrow, doit)) + { + /* Simply change the type of the tree */ - // If caller is worried about possible indirect effects, check - // what we know about the calls in the loop. - // - if (inds != 0) - { - switch (loop->lpAsgCall) - { - case CALLINT_ALL: - return true; - case CALLINT_REF_INDIRS: - return (inds & VR_IND_REF) != 0; - case CALLINT_SCL_INDIRS: - return (inds & VR_IND_SCL) != 0; - case CALLINT_ALL_INDIRS: - return (inds & (VR_IND_REF | VR_IND_SCL)) != 0; - case CALLINT_NONE: + if (doit) + { + tree->gtType = genActualType(dstt); + tree->SetVNs(vnpNarrow); + } + return true; + } return false; + default: - noway_assert(!"Unexpected lpAsgCall value"); + noway_assert(doit == false); + return false; } } @@ -5874,7 +3860,6 @@ PhaseStatus Compiler::optHoistLoopCode() { printf("\n*************** In optHoistLoopCode()\n"); fgDispHandlerTab(); - optPrintLoopTable(); } #endif @@ -5946,15 +3931,9 @@ bool Compiler::optHoistThisLoop(FlowGraphNaturalLoop* loop, LoopHoistContext* ho #ifdef DEBUG if (verbose) { - printf("optHoistThisLoop for loop " FMT_LP " (head: " FMT_BB "):\n", loop->GetIndex(), - loop->GetHeader()->bbNum); + printf("optHoistThisLoop processing "); + FlowGraphNaturalLoop::Dump(loop); printf(" Loop body %s a call\n", sideEffs.ContainsCall ? "contains" : "does not contain"); - printf(" Loop has %s\n", (loop->ExitEdges().size() == 1) ? "single exit" : "multiple exits"); - printf(" Blocks:\n"); - loop->VisitLoopBlocksReversePostOrder([](BasicBlock* bb) { - printf(" " FMT_BB "\n", bb->bbNum); - return BasicBlockVisit::Continue; - }); } #endif @@ -8175,245 +6154,3 @@ PhaseStatus Compiler::optVNBasedDeadStoreRemoval() return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } - -#ifdef DEBUG - -//------------------------------------------------------------------------ -// optAnyChildNotRemoved: Recursively check the child loops of a loop to see if any of them -// are still live (that is, not marked as LPFLG_REMOVED). This check is done when we are -// removing a parent, just to notify that there is something odd about leaving a live child. -// -// Arguments: -// loopNum - the loop number to check -// -bool Compiler::optAnyChildNotRemoved(unsigned loopNum) -{ - assert(loopNum < optLoopCount); - - // Now recursively mark the children. - for (BasicBlock::loopNumber l = optLoopTable[loopNum].lpChild; // - l != BasicBlock::NOT_IN_LOOP; // - l = optLoopTable[l].lpSibling) - { - if (!optLoopTable[l].lpIsRemoved()) - { - return true; - } - - if (optAnyChildNotRemoved(l)) - { - return true; - } - } - - // All children were removed - return false; -} - -#endif // DEBUG - -//------------------------------------------------------------------------ -// optMarkLoopRemoved: Mark the specified loop as removed (some optimization, such as unrolling, has made the -// loop no longer exist). Note that only the given loop is marked as being removed; if it has any children, -// they are not touched (but a warning message is output to the JitDump). -// This method resets the `bbNatLoopNum` field to point to either parent's loop number or NOT_IN_LOOP. -// For consistency, it also updates the child loop's `lpParent` field to have its parent -// -// Arguments: -// loopNum - the loop number to remove -// -void Compiler::optMarkLoopRemoved(unsigned loopNum) -{ -#ifdef DEBUG - if (verbose) - { - printf("Marking loop " FMT_LP " removed\n", loopNum); - optPrintLoopTable(); - } -#endif - - assert(loopNum < optLoopCount); - LoopDsc& loop = optLoopTable[loopNum]; - assert(!loop.lpIsRemoved()); - - for (BasicBlock* const auxBlock : loop.LoopBlocks()) - { - if (auxBlock->bbNatLoopNum == loopNum) - { - JITDUMP("Resetting loop number for " FMT_BB " from " FMT_LP " to " FMT_LP ".\n", auxBlock->bbNum, - auxBlock->bbNatLoopNum, loop.lpParent); - auxBlock->bbNatLoopNum = loop.lpParent; - } - } - - if (loop.lpParent != BasicBlock::NOT_IN_LOOP) - { - // If there is a parent loop, we need to update two things for removed loop `loopNum`: - // 1. Update its siblings so that they no longer point to the `loopNum`. - // 2. Update the children loops to make them point to the parent loop instead. - // - // When we move all the child loops of current loop `loopNum` to its parent, we insert - // those child loops at the same spot where `loopnum` was present in the child chain of - // its parent loop. This is accomplished by updating the existing siblings of `loopNum` - // to now point to the child loops. - // - // If L02 is removed: - // 1. L01's sibling is updated from L02 to L03. - // 2. L03 and L06's parents is updated from L02 to L00. - // 3. L06's sibling is updated from NOT_IN_LOOP to L07. - // - // L00 L00 - // L01 L01 - // L02 => L03 - // L03 L04 - // L04 L05 - // L05 L06 - // L06 L07 - // L07 - // - LoopDsc& parentLoop = optLoopTable[loop.lpParent]; - BasicBlock::loopNumber firstChildLoop = loop.lpChild; - BasicBlock::loopNumber lastChildLoop = BasicBlock::NOT_IN_LOOP; - BasicBlock::loopNumber prevSibling = BasicBlock::NOT_IN_LOOP; - BasicBlock::loopNumber nextSibling = BasicBlock::NOT_IN_LOOP; - for (BasicBlock::loopNumber l = parentLoop.lpChild; l != BasicBlock::NOT_IN_LOOP; l = optLoopTable[l].lpSibling) - { - // We shouldn't see removed loop in loop table. - assert(!optLoopTable[l].lpIsRemoved()); - - nextSibling = optLoopTable[l].lpSibling; - if (l == loopNum) - { - // This condition is not in for-loop just in case there is bad state of loopTable and we - // end up spining infinitely. - break; - } - prevSibling = l; - } - - if (firstChildLoop == BasicBlock::NOT_IN_LOOP) - { - // There are no child loops in `loop`. - // Just update `loop`'s siblings and parentLoop's lpChild, if applicable. - - if (parentLoop.lpChild == loopNum) - { - // If `loop` was the first child - assert(prevSibling == BasicBlock::NOT_IN_LOOP); - - JITDUMP(FMT_LP " has no child loops but is the first child of its parent loop " FMT_LP - ". Update first child to " FMT_LP ".\n", - loopNum, loop.lpParent, nextSibling); - - parentLoop.lpChild = nextSibling; - } - else - { - // `loop` was non-first child - assert(prevSibling != BasicBlock::NOT_IN_LOOP); - - JITDUMP(FMT_LP " has no child loops. Update sibling link " FMT_LP " -> " FMT_LP ".\n", loopNum, - prevSibling, nextSibling); - - optLoopTable[prevSibling].lpSibling = nextSibling; - } - } - else - { - // There are child loops in `loop` that needs to be moved - // under `loop`'s parents. - - if (parentLoop.lpChild == loopNum) - { - // If `loop` was the first child - assert(prevSibling == BasicBlock::NOT_IN_LOOP); - - JITDUMP(FMT_LP " has child loops and is also the first child of its parent loop " FMT_LP - ". Update parent's first child to " FMT_LP ".\n", - loopNum, loop.lpParent, firstChildLoop); - - parentLoop.lpChild = firstChildLoop; - } - else - { - // `loop` was non-first child - assert(prevSibling != BasicBlock::NOT_IN_LOOP); - - JITDUMP(FMT_LP " has child loops. Update sibling link " FMT_LP " -> " FMT_LP ".\n", loopNum, - prevSibling, firstChildLoop); - - optLoopTable[prevSibling].lpSibling = firstChildLoop; - } - - // Update lpParent of all child loops - for (BasicBlock::loopNumber l = firstChildLoop; l != BasicBlock::NOT_IN_LOOP; l = optLoopTable[l].lpSibling) - { - assert(!optLoopTable[l].lpIsRemoved()); - - if (optLoopTable[l].lpSibling == BasicBlock::NOT_IN_LOOP) - { - lastChildLoop = l; - } - - JITDUMP("Resetting parent of loop number " FMT_LP " from " FMT_LP " to " FMT_LP ".\n", l, - optLoopTable[l].lpParent, loop.lpParent); - optLoopTable[l].lpParent = loop.lpParent; - } - - if (lastChildLoop != BasicBlock::NOT_IN_LOOP) - { - JITDUMP(FMT_LP " has child loops. Update sibling link " FMT_LP " -> " FMT_LP ".\n", loopNum, - lastChildLoop, nextSibling); - - optLoopTable[lastChildLoop].lpSibling = nextSibling; - } - else - { - assert(!"There is atleast one loop, but found none."); - } - - // Finally, convey that there are no children of `loopNum` - loop.lpChild = BasicBlock::NOT_IN_LOOP; - } - } - else - { - // If there are no top-level loops, then all the child loops, - // become the top-level loops. - for (BasicBlock::loopNumber l = loop.lpChild; // - l != BasicBlock::NOT_IN_LOOP; // - l = optLoopTable[l].lpSibling) - { - assert(!optLoopTable[l].lpIsRemoved()); - - JITDUMP("Marking loop number " FMT_LP " from " FMT_LP " as top level loop.\n", l, optLoopTable[l].lpParent); - optLoopTable[l].lpParent = BasicBlock::NOT_IN_LOOP; - } - } - - // Unmark any preheader - // - if ((loop.lpFlags & LPFLG_HAS_PREHEAD) != 0) - { - loop.lpHead->RemoveFlags(BBF_LOOP_PREHEADER); - } - - loop.lpFlags |= LPFLG_REMOVED; - -#ifdef DEBUG - if (optAnyChildNotRemoved(loopNum)) - { - JITDUMP("Removed loop " FMT_LP " has one or more live children\n", loopNum); - } - - if (verbose) - { - printf("Removed " FMT_LP "\n", loopNum); - optPrintLoopTable(); - } - -// Note: we can't call `fgDebugCheckLoopTable()` here because if there are live children, it will assert. -// Assume the caller is going to fix up the table and `bbNatLoopNum` block annotations before the next time -// `fgDebugCheckLoopTable()` is called. -#endif // DEBUG -} diff --git a/src/coreclr/jit/phase.cpp b/src/coreclr/jit/phase.cpp index 25371e203588a0..717d0a7d270d5e 100644 --- a/src/coreclr/jit/phase.cpp +++ b/src/coreclr/jit/phase.cpp @@ -158,7 +158,7 @@ void Phase::PostPhase(PhaseStatus status) if ((comp->activePhaseChecks & PhaseChecks::CHECK_LOOPS) == PhaseChecks::CHECK_LOOPS) { - comp->fgDebugCheckLoopTable(); + comp->fgDebugCheckLoops(); } if ((comp->activePhaseChecks & PhaseChecks::CHECK_PROFILE) == PhaseChecks::CHECK_PROFILE) diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp index 235a5fc1d5f40c..0201a62ddf7b7f 100644 --- a/src/coreclr/jit/redundantbranchopts.cpp +++ b/src/coreclr/jit/redundantbranchopts.cpp @@ -1007,42 +1007,6 @@ bool Compiler::optJumpThreadCheck(BasicBlock* const block, BasicBlock* const dom return false; } - if (optLoopTableValid) - { - // If block is a loop header, skip jump threading. - // - // This is an artificial limitation to ensure that subsequent loop table valididity - // checking can pass. We do not expect a loop entry to have multiple non-loop predecessors. - // - // This only blocks jump threading in a small number of cases. - // Revisit once we can ensure that loop headers are not critical blocks. - // - // Likewise we can't properly update the loop table if we thread the entry block. - // Not clear how much impact this has. - // - for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) - { - const LoopDsc& loop = optLoopTable[loopNum]; - - if (loop.lpIsRemoved()) - { - continue; - } - - if (block == loop.lpHead) - { - JITDUMP(FMT_BB " is the header for " FMT_LP "; no threading\n", block->bbNum, loopNum); - return false; - } - - if (block == loop.lpEntry) - { - JITDUMP(FMT_BB " is the entry for " FMT_LP "; no threading\n", block->bbNum, loopNum); - return false; - } - } - } - // Verify that dom block dominates all of block's predecessors. // // This will initially be true but if we jump thread through From 99b621f7b5db434e1fbd4a09550f55e316cfcb98 Mon Sep 17 00:00:00 2001 From: Tymoteusz Wenerski Date: Mon, 22 Jan 2024 10:56:34 +0100 Subject: [PATCH 156/189] [RISC-V] Enable On Stack Replacement (#96558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [RISC-V] Implement On Stack Replacement Note: Pinned local test is failing. * [RISC-V] Apply suggestions from code review Co-authored-by: Tomasz Sowiński * [RISC-V] apply jit-format * [RISC-V] Cosmetic changes after code review * [RISC-V] Changes assuming memory page is always equal 4KiB * [RISC-V] Remove stack probing * [RISC-V] Replace GetEmitter() with emit * [RISC-V] Sync frame type 1 in genPushCalleeSavedRegisters with genPopCalleeSavedRegisters * [RISC-V] Fix assembly emmited by genStackProbe * [RISC-V] Apply jit-formatter --------- Co-authored-by: Tomasz Sowiński --- src/coreclr/clrdefinitions.cmake | 4 +- src/coreclr/inc/clrconfigvalues.h | 6 +- src/coreclr/jit/codegen.h | 12 +- src/coreclr/jit/codegencommon.cpp | 30 +- src/coreclr/jit/codegenriscv64.cpp | 1436 ++++++++++++++++++---------- src/coreclr/jit/compiler.cpp | 4 +- src/coreclr/jit/gcencode.cpp | 4 +- src/coreclr/jit/jitconfigvalues.h | 4 +- src/coreclr/jit/lclvars.cpp | 33 +- src/coreclr/jit/targetriscv64.h | 3 + 10 files changed, 989 insertions(+), 547 deletions(-) diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index c180198a4db4a9..fb8d095b5606d7 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -184,9 +184,9 @@ endif(FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) add_definitions(-DFEATURE_SVR_GC) add_definitions(-DFEATURE_SYMDIFF) add_compile_definitions(FEATURE_TIERED_COMPILATION) -if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64) +if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) add_compile_definitions(FEATURE_ON_STACK_REPLACEMENT) -endif (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64) +endif (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_LOONGARCH64 OR CLR_CMAKE_TARGET_ARCH_RISCV64) add_compile_definitions(FEATURE_PGO) if (CLR_CMAKE_TARGET_WIN32) add_definitions(-DFEATURE_TYPEEQUIVALENCE) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6035bcd509425a..12a1e706602cc5 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -569,11 +569,11 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_HillClimbing_GainExponent, #endif // _DEBUG RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredCompilation, W("TieredCompilation"), 1, "Enables tiered compilation") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_QuickJit, W("TC_QuickJit"), 1, "For methods that would be jitted, enable using quick JIT when appropriate.") -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_QuickJitForLoops, W("TC_QuickJitForLoops"), 1, "When quick JIT is enabled, quick JIT may also be used for methods that contain loops.") -#else // !(defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) +#else // !(defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) || defined(TARGET_RISCV64) RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TC_QuickJitForLoops, W("TC_QuickJitForLoops"), 0, "When quick JIT is enabled, quick JIT may also be used for methods that contain loops.") -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_AggressiveTiering, W("TC_AggressiveTiering"), 0, "Transition through tiers aggressively.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_CallCountThreshold, W("TC_CallCountThreshold"), TC_CallCountThreshold, "Number of times a method must be called in tier 0 after which it is promoted to the next tier.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TC_CallCountingDelayMs, W("TC_CallCountingDelayMs"), TC_CallCountingDelayMs, "A perpetual delay in milliseconds that is applied to call counting in tier 0 and jitting at higher tiers, while there is startup-like activity.") diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index e1af485a0eb4f6..386183737d760e 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -265,7 +265,7 @@ class CodeGen final : public CodeGenInterface void genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbered, RegState* regState); #endif void genEnregisterIncomingStackArgs(); -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) void genEnregisterOSRArgsAndLocals(regNumber initReg, bool* pInitRegZeroed); #else void genEnregisterOSRArgsAndLocals(); @@ -345,6 +345,10 @@ class CodeGen final : public CodeGenInterface void genOSRSaveRemainingCalleeSavedRegisters(); #endif // TARGET_AMD64 +#if defined(TARGET_RISCV64) + void genStackProbe(ssize_t frameSize, regNumber rOffset, regNumber rLimit, regNumber rPageSize); +#endif + void genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pInitRegZeroed, regMaskTP maskArgRegsLiveIn); void genPoisonFrame(regMaskTP bbRegLiveIn); @@ -450,11 +454,11 @@ class CodeGen final : public CodeGenInterface regMaskTP fiSaveRegs; // Set of callee-saved registers saved in the funclet prolog (includes RA) int fiFunction_CallerSP_to_FP_delta; // Delta between caller SP and the frame pointer in the parent function // (negative) - int fiSP_to_FPRA_save_delta; // FP/RA register save offset from SP (positive) + int fiSP_to_CalleeSaved_delta; // CalleeSaved register save offset from SP (positive) + int fiCalleeSavedPadding; // CalleeSaved offset padding (positive) int fiSP_to_PSP_slot_delta; // PSP slot offset from SP (positive) int fiCallerSP_to_PSP_slot_delta; // PSP slot offset from Caller SP (negative) - int fiFrameType; // Funclet frame types are numbered. See genFuncletProlog() for details. - int fiSpDelta1; // Stack pointer delta 1 (negative) + int fiSpDelta; // Stack pointer delta (negative) }; FuncletFrameInfoDsc genFuncletInfo; diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index b12b584038c530..133403e2f9d2bd 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -4750,7 +4750,7 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg, // initReg -- scratch register to use if needed // pInitRegZeroed -- [IN,OUT] if init reg is zero (on entry/exit) // -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) void CodeGen::genEnregisterOSRArgsAndLocals(regNumber initReg, bool* pInitRegZeroed) #else void CodeGen::genEnregisterOSRArgsAndLocals() @@ -4891,7 +4891,7 @@ void CodeGen::genEnregisterOSRArgsAndLocals() GetEmitter()->emitIns_R_AR(ins_Load(lclTyp), size, varDsc->GetRegNum(), genFramePointerReg(), offset); -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Patchpoint offset is from top of Tier0 frame // @@ -4923,7 +4923,7 @@ void CodeGen::genEnregisterOSRArgsAndLocals() genInstrWithConstant(ins_Load(lclTyp), size, varDsc->GetRegNum(), genFramePointerReg(), offset, initReg); *pInitRegZeroed = false; -#endif +#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 } } @@ -5530,7 +5530,7 @@ void CodeGen::genFnProlog() psiBegProlog(); } -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // For arm64 OSR, emit a "phantom prolog" to account for the actions taken // in the tier0 frame that impact FP and SP on entry to the OSR method. // @@ -5545,7 +5545,7 @@ void CodeGen::genFnProlog() // SP is tier0 method's SP. compiler->unwindAllocStack(tier0FrameSize); } -#endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #ifdef DEBUG @@ -5875,13 +5875,25 @@ void CodeGen::genFnProlog() { initReg = REG_SCRATCH; } +#elif defined(TARGET_RISCV64) + // For RISC-V64 OSR root frames, we may need a scratch register for large + // offset addresses. Use a register that won't be allocated. + if (isRoot && compiler->opts.IsOSR()) + { + initReg = REG_SCRATCH; // REG_T0 + } #endif -#ifndef TARGET_LOONGARCH64 +#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) // For LoongArch64's OSR root frames, we may need a scratch register for large // offset addresses. But this does not conflict with the REG_PINVOKE_FRAME. + // + // RISC-V64's OSR root frames are similar to LoongArch64's. In this case + // REG_SCRATCH also shouldn't conflict with REG_PINVOKE_FRAME, even if + // technically they are the same register - REG_T0. + // noway_assert(!compiler->compMethodRequiresPInvokeFrame() || (initReg != REG_PINVOKE_FRAME)); -#endif +#endif // !TARGET_LOONGARCH64 && !TARGET_RISCV64 #if defined(TARGET_AMD64) // If we are a varargs call, in order to set up the arguments correctly this @@ -6192,7 +6204,7 @@ void CodeGen::genFnProlog() // Otherwise we'll do some of these fetches twice. // CLANG_FORMAT_COMMENT_ANCHOR; -#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) genEnregisterOSRArgsAndLocals(initReg, &initRegZeroed); #else genEnregisterOSRArgsAndLocals(); @@ -6250,7 +6262,7 @@ void CodeGen::genFnProlog() assignIncomingRegisterArgs(&intRegState); #else assignIncomingRegisterArgs(&intRegState); -#endif +#endif // TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64 #endif // TARGET_LOONGARCH64 || TARGET_RISCV64 diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 3e2788c385a733..05f7a2c11f4432 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -20,7 +20,34 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "lower.h" #include "gcinfo.h" #include "gcinfoencoder.h" +#include "patchpointinfo.h" +//------------------------------------------------------------------------ +// genInstrWithConstant: we will typically generate one instruction +// +// ins reg1, reg2, imm +// +// However the imm might not fit as a directly encodable immediate, +// when it doesn't fit we generate extra instruction(s) that sets up +// the 'regTmp' with the proper immediate value. +// +// mov regTmp, imm +// ins reg1, reg2, regTmp +// +// Arguments: +// ins - instruction +// attr - operation size and GC attribute +// reg1, reg2 - first and second register operands +// imm - immediate value (third operand when it fits) +// tmpReg - temp register to use when the 'imm' doesn't fit. Can be REG_NA +// if caller knows for certain the constant will fit. +// inUnwindRegion - true if we are in a prolog/epilog region with unwind codes. +// Default: false. +// +// Return Value: +// returns true if the immediate was small enough to be encoded inside instruction. If not, +// returns false meaning the immediate was too large and tmpReg was used and modified. +// bool CodeGen::genInstrWithConstant(instruction ins, emitAttr attr, regNumber reg1, @@ -99,10 +126,25 @@ bool CodeGen::genInstrWithConstant(instruction ins, return immFitsInIns; } +//------------------------------------------------------------------------ +// genStackPointerAdjustment: add a specified constant value to the stack pointer in either the prolog +// or the epilog. The unwind codes for the generated instructions are produced. An available temporary +// register is required to be specified, in case the constant is too large to encode in an "add" +// instruction, such that we need to load the constant +// into a register first, before using it. +// +// Arguments: +// spDelta - the value to add to SP (can be negative) +// tmpReg - an available temporary register +// pTmpRegIsZero - If we use tmpReg, and pTmpRegIsZero is non-null, we set *pTmpRegIsZero to 'false'. +// Otherwise, we don't touch it. +// reportUnwindData - If true, report the change in unwind data. Otherwise, do not report it. +// +// Return Value: +// None. void CodeGen::genStackPointerAdjustment(ssize_t spDelta, regNumber tmpReg, bool* pTmpRegIsZero, bool reportUnwindData) { - // Even though INS_addi is specified here, the encoder will choose either - // an INS_add_d or an INS_addi_d and encode the immediate as a positive value + // Even though INS_addi is specified here, the encoder will replace it with INS_add // bool wasTempRegisterUsedForImm = !genInstrWithConstant(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, spDelta, tmpReg, true); @@ -126,6 +168,26 @@ void CodeGen::genStackPointerAdjustment(ssize_t spDelta, regNumber tmpReg, bool* } } +//------------------------------------------------------------------------ +// genPrologSaveRegPair: Save a pair of general-purpose or floating-point/SIMD registers in a function or funclet +// prolog. If possible, we use pre-indexed addressing to adjust SP and store the registers with a single instruction. +// The caller must ensure that we can use the STP instruction, and that spOffset will be in the legal range for that +// instruction. +// +// Arguments: +// reg1 - First register of pair to save. +// reg2 - Second register of pair to save. +// spOffset - The offset from SP to store reg1 (must be positive or zero). +// spDelta - If non-zero, the amount to add to SP before the register saves (must be negative or +// zero). +// useSaveNextPair - True if the last prolog instruction was to save the previous register pair. This +// allows us to emit the "save_next" unwind code. +// tmpReg - An available temporary register. Needed for the case of large frames. +// pTmpRegIsZero - If we use tmpReg, and pTmpRegIsZero is non-null, we set *pTmpRegIsZero to 'false'. +// Otherwise, we don't touch it. +// +// Return Value: +// None. void CodeGen::genPrologSaveRegPair(regNumber reg1, regNumber reg2, int spOffset, @@ -148,7 +210,7 @@ void CodeGen::genPrologSaveRegPair(regNumber reg1, if (spDelta != 0) { - // generate addi.d SP,SP,-imm + // generate addi SP,SP,-imm genStackPointerAdjustment(spDelta, tmpReg, pTmpRegIsZero, /* reportUnwindData */ true); assert((spDelta + spOffset + 16) <= 0); @@ -156,13 +218,36 @@ void CodeGen::genPrologSaveRegPair(regNumber reg1, assert(spOffset <= 2031); // 2047-16 } - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); + emitter* emit = GetEmitter(); + + // sd reg1, #spOffset(sp) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); compiler->unwindSaveReg(reg1, spOffset); - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg2, REG_SPBASE, spOffset + 8); + // sd reg2, #(spOffset + 8)(sp) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg2, REG_SPBASE, spOffset + 8); compiler->unwindSaveReg(reg2, spOffset + 8); } +//------------------------------------------------------------------------ +// genPrologSaveReg: Like genPrologSaveRegPair, but for a single register. Save a single general-purpose or +// floating-point/SIMD register in a function or funclet prolog. Note that if we wish to change SP (i.e., spDelta != 0), +// then spOffset must be 8. This is because otherwise we would create an alignment hole above the saved register, not +// below it, which we currently don't support. This restriction could be loosened if the callers change to handle it +// (and this function changes to support using pre-indexed SD addressing). The caller must ensure that we can use the +// SD instruction, and that spOffset will be in the legal range for that instruction. +// +// Arguments: +// reg1 - Register to save. +// spOffset - The offset from SP to store reg1 (must be positive or zero). +// spDelta - If non-zero, the amount to add to SP before the register saves (must be negative or +// zero). +// tmpReg - An available temporary register. Needed for the case of large frames. +// pTmpRegIsZero - If we use tmpReg, and pTmpRegIsZero is non-null, we set *pTmpRegIsZero to 'false'. +// Otherwise, we don't touch it. +// +// Return Value: +// None. void CodeGen::genPrologSaveReg(regNumber reg1, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero) { assert(spOffset >= 0); @@ -177,14 +262,37 @@ void CodeGen::genPrologSaveReg(regNumber reg1, int spOffset, int spDelta, regNum if (spDelta != 0) { - // generate daddiu SP,SP,-imm + // generate addi SP,SP,-imm genStackPointerAdjustment(spDelta, tmpReg, pTmpRegIsZero, /* reportUnwindData */ true); } - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); + emitter* emit = GetEmitter(); + + // sd reg1, #spOffset(sp) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); compiler->unwindSaveReg(reg1, spOffset); } +//------------------------------------------------------------------------ +// genEpilogRestoreRegPair: This is the opposite of genPrologSaveRegPair(), run in the epilog instead of the prolog. +// The stack pointer adjustment, if requested, is done after the register restore, using post-index addressing. +// The caller must ensure that we can use the LDP instruction, and that spOffset will be in the legal range for that +// instruction. +// +// Arguments: +// reg1 - First register of pair to restore. +// reg2 - Second register of pair to restore. +// spOffset - The offset from SP to load reg1 (must be positive or zero). +// spDelta - If non-zero, the amount to add to SP after the register restores (must be positive or +// zero). +// useSaveNextPair - True if the last prolog instruction was to save the previous register pair. This +// allows us to emit the "save_next" unwind code. +// tmpReg - An available temporary register. Needed for the case of large frames. +// pTmpRegIsZero - If we use tmpReg, and pTmpRegIsZero is non-null, we set *pTmpRegIsZero to 'false'. +// Otherwise, we don't touch it. +// +// Return Value: +// None. void CodeGen::genEpilogRestoreRegPair(regNumber reg1, regNumber reg2, int spOffset, @@ -205,29 +313,49 @@ void CodeGen::genEpilogRestoreRegPair(regNumber reg1, ins = INS_fld; } + emitter* emit = GetEmitter(); + if (spDelta != 0) { assert(!useSaveNextPair); - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg2, REG_SPBASE, spOffset + 8); + // ld reg2, #(spOffset + 8)(SP) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg2, REG_SPBASE, spOffset + 8); compiler->unwindSaveReg(reg2, spOffset + 8); - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); + // ld reg1, #spOffset(SP) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); compiler->unwindSaveReg(reg1, spOffset); - // generate daddiu SP,SP,imm + // generate addi SP,SP,imm genStackPointerAdjustment(spDelta, tmpReg, pTmpRegIsZero, /* reportUnwindData */ true); } else { - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg2, REG_SPBASE, spOffset + 8); + // ld reg2, #(spOffset + 8)(SP) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg2, REG_SPBASE, spOffset + 8); compiler->unwindSaveReg(reg2, spOffset + 8); - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); + // ld reg1, #spOffset(SP) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); compiler->unwindSaveReg(reg1, spOffset); } } +//------------------------------------------------------------------------ +// genEpilogRestoreReg: The opposite of genPrologSaveReg(), run in the epilog instead of the prolog. +// +// Arguments: +// reg1 - Register to restore. +// spOffset - The offset from SP to restore reg1 (must be positive or zero). +// spDelta - If non-zero, the amount to add to SP after the register restores (must be positive or +// zero). +// tmpReg - An available temporary register. Needed for the case of large frames. +// pTmpRegIsZero - If we use tmpReg, and pTmpRegIsZero is non-null, we set *pTmpRegIsZero to 'false'. +// Otherwise, we don't touch it. +// +// Return Value: +// None. void CodeGen::genEpilogRestoreReg(regNumber reg1, int spOffset, int spDelta, regNumber tmpReg, bool* pTmpRegIsZero) { assert(spOffset >= 0); @@ -240,22 +368,38 @@ void CodeGen::genEpilogRestoreReg(regNumber reg1, int spOffset, int spDelta, reg ins = INS_fld; } + emitter* emit = GetEmitter(); + if (spDelta != 0) { - // ld reg1, offset(SP) - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); + // ld reg1, #spOffset(SP) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); compiler->unwindSaveReg(reg1, spOffset); - // generate add SP,SP,imm + // generate addi SP,SP,imm genStackPointerAdjustment(spDelta, tmpReg, pTmpRegIsZero, /* reportUnwindData */ true); } else { - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); + // ld reg1 #spOffset(SP) + emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, REG_SPBASE, spOffset); compiler->unwindSaveReg(reg1, spOffset); } } +//------------------------------------------------------------------------ +// genBuildRegPairsStack: Build a stack of register pairs for prolog/epilog save/restore for the given mask. +// The first register pair will contain the lowest register. Register pairs will combine neighbor +// registers in pairs. If it can't be done (for example if we have a hole or this is the last reg in a mask with +// odd number of regs) then the second element of that RegPair will be REG_NA. +// +// Arguments: +// regsMask - a mask of registers for prolog/epilog generation; +// regStack - a regStack instance to build the stack in, used to save temp copyings. +// +// Return value: +// no return value; the regStack argument is modified. +// // static void CodeGen::genBuildRegPairsStack(regMaskTP regsMask, ArrayStack* regStack) { @@ -310,6 +454,19 @@ void CodeGen::genBuildRegPairsStack(regMaskTP regsMask, ArrayStack* reg genSetUseSaveNextPairs(regStack); } +//------------------------------------------------------------------------ +// genSetUseSaveNextPairs: Set useSaveNextPair for each RegPair on the stack which unwind info can be encoded as +// save_next code. +// +// Arguments: +// regStack - a regStack instance to set useSaveNextPair. +// +// Notes: +// We can use save_next for RegPair(N, N+1) only when we have sequence like (N-2, N-1), (N, N+1). +// In this case in the prolog save_next for (N, N+1) refers to save_pair(N-2, N-1); +// in the epilog the unwinder will search for the first save_pair (N-2, N-1) +// and then go back to the first save_next (N, N+1) to restore it first. +// // static void CodeGen::genSetUseSaveNextPairs(ArrayStack* regStack) { @@ -338,6 +495,18 @@ void CodeGen::genSetUseSaveNextPairs(ArrayStack* regStack) } } +//------------------------------------------------------------------------ +// genGetSlotSizeForRegsInMask: Get the stack slot size appropriate for the register type from the mask. +// +// Arguments: +// regsMask - a mask of registers for prolog/epilog generation. +// +// Return value: +// stack slot size in bytes. +// +// Note: Because int and float register type sizes match we can call this function with a mask that includes both. +// +// static int CodeGen::genGetSlotSizeForRegsInMask(regMaskTP regsMask) { assert((regsMask & (RBM_CALLEE_SAVED | RBM_FP | RBM_RA)) == regsMask); // Do not expect anything else. @@ -346,6 +515,14 @@ int CodeGen::genGetSlotSizeForRegsInMask(regMaskTP regsMask) return REGSIZE_BYTES; } +//------------------------------------------------------------------------ +// genSaveCalleeSavedRegisterGroup: Saves the group of registers described by the mask. +// +// Arguments: +// regsMask - a mask of registers for prolog generation; +// spDelta - if non-zero, the amount to add to SP before the first register save (or together with it); +// spOffset - the offset from SP that is the beginning of the callee-saved register area; +// void CodeGen::genSaveCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, int spOffset) { const int slotSize = genGetSlotSizeForRegsInMask(regsMask); @@ -353,21 +530,23 @@ void CodeGen::genSaveCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, i ArrayStack regStack(compiler->getAllocator(CMK_Codegen)); genBuildRegPairsStack(regsMask, ®Stack); + regNumber tempReg = rsGetRsvdReg(); + for (int i = 0; i < regStack.Height(); ++i) { RegPair regPair = regStack.Bottom(i); if (regPair.reg2 != REG_NA) { // We can use two SD instructions. - genPrologSaveRegPair(regPair.reg1, regPair.reg2, spOffset, spDelta, regPair.useSaveNextPair, rsGetRsvdReg(), + genPrologSaveRegPair(regPair.reg1, regPair.reg2, spOffset, spDelta, regPair.useSaveNextPair, tempReg, nullptr); - spOffset += 2 * slotSize; + spOffset += slotSize << 1; } else { // No register pair; we use a SD instruction. - genPrologSaveReg(regPair.reg1, spOffset, spDelta, rsGetRsvdReg(), nullptr); + genPrologSaveReg(regPair.reg1, spOffset, spDelta, tempReg, nullptr); spOffset += slotSize; } @@ -375,6 +554,37 @@ void CodeGen::genSaveCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, i } } +//------------------------------------------------------------------------ +// genSaveCalleeSavedRegistersHelp: Save the callee-saved registers in 'regsToSaveMask' to the stack frame +// in the function or funclet prolog. Registers are saved in register number order from low addresses +// to high addresses. This means that integer registers are saved at lower addresses than floatint-point/SIMD +// registers. +// +// If establishing frame pointer chaining, it must be done after saving the callee-saved registers. +// +// We can only use the instructions that are allowed by the unwind codes. The caller ensures that +// there is enough space on the frame to store these registers, and that the store instructions +// we need to use (SD) are encodable with the stack-pointer immediate offsets we need to use. +// +// The caller can tell us to fold in a stack pointer adjustment, which we will do with the first instruction. +// Note that the stack pointer adjustment must be by a multiple of 16 to preserve the invariant that the +// stack pointer is always 16 byte aligned. If we are saving an odd number of callee-saved +// registers, though, we will have an empty alignment slot somewhere. It turns out we will put +// it below (at a lower address) the callee-saved registers, as that is currently how we +// do frame layout. This means that the first stack offset will be 8 and the stack pointer +// adjustment must be done by an ADDI (or ADD), and not folded in to a pre-indexed store. +// +// Arguments: +// regsToSaveMask - The mask of callee-saved registers to save. If empty, this function does nothing. +// lowestCalleeSavedOffset - The offset from SP that is the beginning of the callee-saved register area. Note that +// if non-zero spDelta, then this is the offset of the first save *after* that +// SP adjustment. +// spDelta - If non-zero, the amount to add to SP before the register saves (must be negative or +// zero). +// +// Notes: +// The save set can not contain FP/RA in which case FP/RA is saved along with the other callee-saved registers. +// void CodeGen::genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowestCalleeSavedOffset, int spDelta) { assert(spDelta <= 0); @@ -386,17 +596,17 @@ void CodeGen::genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowe { // Currently this is the case for varargs only // whose size is MAX_REG_ARG * REGSIZE_BYTES = 64 bytes. + // addi sp, sp, #spDelta genStackPointerAdjustment(spDelta, rsGetRsvdReg(), nullptr, /* reportUnwindData */ true); } return; } - assert((spDelta % 16) == 0); + assert((spDelta % STACK_ALIGN) == 0); assert(regsToSaveCount <= genCountBits(RBM_CALLEE_SAVED)); // Save integer registers at higher addresses than floating-point registers. - regMaskTP maskSaveRegsFloat = regsToSaveMask & RBM_ALLFLOAT; regMaskTP maskSaveRegsInt = regsToSaveMask & ~maskSaveRegsFloat; @@ -414,6 +624,14 @@ void CodeGen::genSaveCalleeSavedRegistersHelp(regMaskTP regsToSaveMask, int lowe } } +//------------------------------------------------------------------------ +// genRestoreCalleeSavedRegisterGroup: Restores the group of registers described by the mask. +// +// Arguments: +// regsMask - a mask of registers for epilog generation; +// spDelta - if non-zero, the amount to add to SP after the last register restore (or together with it); +// spOffset - the offset from SP that is the beginning of the callee-saved register area; +// void CodeGen::genRestoreCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta, int spOffset) { const int slotSize = genGetSlotSizeForRegsInMask(regsMask); @@ -421,6 +639,8 @@ void CodeGen::genRestoreCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta ArrayStack regStack(compiler->getAllocator(CMK_Codegen)); genBuildRegPairsStack(regsMask, ®Stack); + regNumber tempReg = rsGetRsvdReg(); + int stackDelta = 0; for (int i = 0; i < regStack.Height(); ++i) { @@ -436,19 +656,47 @@ void CodeGen::genRestoreCalleeSavedRegisterGroup(regMaskTP regsMask, int spDelta RegPair regPair = regStack.Top(i); if (regPair.reg2 != REG_NA) { - spOffset -= 2 * slotSize; + spOffset -= slotSize << 1; - genEpilogRestoreRegPair(regPair.reg1, regPair.reg2, spOffset, stackDelta, regPair.useSaveNextPair, - rsGetRsvdReg(), nullptr); + genEpilogRestoreRegPair(regPair.reg1, regPair.reg2, spOffset, stackDelta, regPair.useSaveNextPair, tempReg, + nullptr); } else { spOffset -= slotSize; - genEpilogRestoreReg(regPair.reg1, spOffset, stackDelta, rsGetRsvdReg(), nullptr); + genEpilogRestoreReg(regPair.reg1, spOffset, stackDelta, tempReg, nullptr); } } } +//------------------------------------------------------------------------ +// genRestoreCalleeSavedRegistersHelp: Restore the callee-saved registers in 'regsToRestoreMask' from the stack frame +// in the function or funclet epilog. This exactly reverses the actions of genSaveCalleeSavedRegistersHelp(). +// +// Arguments: +// regsToRestoreMask - The mask of callee-saved registers to restore. If empty, this function does nothing. +// lowestCalleeSavedOffset - The offset from SP that is the beginning of the callee-saved register area. +// spDelta - If non-zero, the amount to add to SP after the register restores (must be positive or +// zero). +// +// Here's an example restore sequence: +// ld s11, #xxx(sp) +// ld s10, #xxx(sp) +// ld s9, #xxx(sp) +// ld s8, #xxx(sp) +// ld s7, #xxx(sp) +// ld s6, #xxx(sp) +// ld s5, #xxx(sp) +// ld s4, #xxx(sp) +// ld s3, #xxx(sp) +// ld s2, #xxx(sp) +// ld s1, #xxx(sp) +// +// Note you call the unwind functions specifying the prolog operation that is being un-done. So, for example, when +// generating a post-indexed load, you call the unwind function for specifying the corresponding preindexed store. +// +// Return Value: +// None. void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, int lowestCalleeSavedOffset, int spDelta) { assert(spDelta >= 0); @@ -464,7 +712,7 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in return; } - assert((spDelta % 16) == 0); + assert((spDelta % STACK_ALIGN) == 0); // We also can restore FP and RA, even though they are not in RBM_CALLEE_SAVED. assert(regsToRestoreCount <= genCountBits(RBM_CALLEE_SAVED | RBM_FP | RBM_RA)); @@ -495,14 +743,102 @@ void CodeGen::genRestoreCalleeSavedRegistersHelp(regMaskTP regsToRestoreMask, in } } +// clang-format off +/***************************************************************************** + * + * Generates code for an EH funclet prolog. + * + * Funclets have the following incoming arguments: + * + * catch: a0 = the exception object that was caught (see GT_CATCH_ARG) + * filter: a0 = the exception object to filter (see GT_CATCH_ARG), a1 = CallerSP of the containing function + * finally/fault: none + * + * Funclets set the following registers on exit: + * + * catch: a0 = the address at which execution should resume (see BBJ_EHCATCHRET) + * filter: a0 = non-zero if the handler should handle the exception, zero otherwise (see GT_RETFILT) + * finally/fault: none + * + * The RISC-V64 funclet prolog is the following (Note: #framesz is total funclet frame size, + * including everything; #outsz is outgoing argument space. #framesz must be a multiple of 16): + * + * Frame type liking: + * addi sp, sp, -#framesz ; establish the frame + * sd s1, #outsz(sp) ; save callee-saved registers, as necessary + * sd s2, #(outsz+8)(sp) + * sd ra, #(outsz+?)(sp) ; save RA (8 bytes) + * sd fp, #(outsz+?+8)(sp) ; save FP (8 bytes) + * + * The funclet frame layout: + * + * | | + * |-----------------------| + * | incoming arguments | + * +=======================+ <---- Caller's SP + * | Arguments Or | // if needed + * | Varargs regs space | // Only for varargs functions; NYI on RV64 + * |-----------------------| + * | MonitorAcquired | // 8 bytes; for synchronized methods + * |-----------------------| + * | PSP slot | // 8 bytes (omitted in NativeAOT ABI) + * |-----------------------| + * ~ alignment padding ~ // To make the whole frame 16 byte aligned + * |-----------------------| + * | Saved FP | // 8 bytes + * |-----------------------| + * | Saved RA | // 8 bytes + * |-----------------------| + * |Callee saved registers | // multiple of 8 bytes, not includting RA/FP + * |-----------------------| + * | Outgoing arg space | // multiple of 8 bytes; if required (i.e., #outsz != 0) + * |-----------------------| <---- Ambient SP + * | | | + * ~ | Stack grows ~ + * | | downward | + * V + * + * Note, that SP only change once. That means, there will be a maximum of one alignment slot needed. + * Also remember, the stack oiubter needs to be 16 byte aligned at all times. + * The size of the PSP slot plus callee-saved registers space is a maximum of 280 bytes: + * + * RA,FP registers + * 11 int callee-saved register s1-s11 + * 12 float callee-saved registers f8-f9, f18-f27 + * 8 saved integer argument registers a0-a7, if varargs function support. + * 1 PSP slot + * 1 alignment slot or monitor acquired slot + * == 35 slots * 8 bytes = 280 bytes. + * + * The outgoing argument size, however, can be very large, if we call a function that takes a large number of + * arguments (note that we currently use the same outgoing argument space size in the funclet as for the main + * function, even if the funclet doesn't have any calls, or has a much smaller, or larger, maximum number of + * outgoing arguments for any call). In that case, we need to 16-byte align the initial change to SP, before + * saving off the callee-saved registers and establishing the PSPsym, so we can use the limited immediate offset + * encodings we have available, before doing another 16-byte aligned SP adjustment to create the outgoing argument + * space. Both changes to SP might need to add alignment padding. + * + * An example epilog sequence: + * addi sp, sp, #outsz ; if any outgoing argument space + * ld s1, #(xxx-8)(sp) ; restore callee-saved registers + * ld s2, #xxx(sp) + * ld ra, #(xxx+?-8)(sp) ; restore RA + * ld fp, #(xxx+?)(sp) ; restore FP + * addi sp, sp, #framesz + * jarl zero, ra + */ // clang-format on void CodeGen::genFuncletProlog(BasicBlock* block) { #ifdef DEBUG if (verbose) + { printf("*************** In genFuncletProlog()\n"); + } #endif + // TODO-RISCV64: Implement varargs (NYI_RISCV64) + // TODO-RISCV64-CQ: We can use C extension for optimization assert(block != NULL); assert(block->HasFlag(BBF_FUNCLET_BEG)); @@ -513,15 +849,10 @@ void CodeGen::genFuncletProlog(BasicBlock* block) compiler->unwindBegProlog(); - regMaskTP maskSaveRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT; - regMaskTP maskSaveRegsInt = genFuncletInfo.fiSaveRegs & ~maskSaveRegsFloat; - - // Funclets must always save RA and FP, since when we have funclets we must have an FP frame. - assert((maskSaveRegsInt & RBM_RA) != 0); - assert((maskSaveRegsInt & RBM_FP) != 0); + const bool isFilter = (block->bbCatchTyp == BBCT_FILTER); + const int frameSize = genFuncletInfo.fiSpDelta; - bool isFilter = (block->bbCatchTyp == BBCT_FILTER); - int frameSize = genFuncletInfo.fiSpDelta1; + assert(frameSize < 0); regMaskTP maskArgRegsLiveIn; if (isFilter) @@ -537,62 +868,53 @@ void CodeGen::genFuncletProlog(BasicBlock* block) maskArgRegsLiveIn = RBM_A0; } -#ifdef DEBUG - if (compiler->opts.disAsm) - { - printf("DEBUG: CodeGen::genFuncletProlog, frameType:%d\n\n", genFuncletInfo.fiFrameType); - } -#endif + regMaskTP maskSaveRegs = genFuncletInfo.fiSaveRegs & RBM_CALLEE_SAVED; + int regsSavedSize = (compiler->compCalleeRegsPushed - 2) << 3; - int offset = 0; - if (genFuncletInfo.fiFrameType == 1) - { - // fiFrameType constraints: - assert(frameSize < 0); - assert(frameSize >= -2048); + int calleeSavedDelta = genFuncletInfo.fiSP_to_CalleeSaved_delta; - assert(genFuncletInfo.fiSP_to_FPRA_save_delta < 2040); - genStackPointerAdjustment(frameSize, rsGetRsvdReg(), nullptr, /* reportUnwindData */ true); + emitter* emit = GetEmitter(); - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, genFuncletInfo.fiSP_to_FPRA_save_delta); - compiler->unwindSaveReg(REG_FP, genFuncletInfo.fiSP_to_FPRA_save_delta); + if (calleeSavedDelta + regsSavedSize + genFuncletInfo.fiCalleeSavedPadding <= 2040) + { + calleeSavedDelta += genFuncletInfo.fiCalleeSavedPadding; + + // addi sp, sp, #frameSize + genStackPointerAdjustment(frameSize, REG_SCRATCH, nullptr, /* reportUnwindData */ true); - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, genFuncletInfo.fiSP_to_FPRA_save_delta + 8); - compiler->unwindSaveReg(REG_RA, genFuncletInfo.fiSP_to_FPRA_save_delta + 8); + genSaveCalleeSavedRegistersHelp(maskSaveRegs, calleeSavedDelta, 0); + calleeSavedDelta += regsSavedSize; - maskSaveRegsInt &= ~(RBM_RA | RBM_FP); // We've saved these now + // sd ra, #calleeSavedDelta(sp) + emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, calleeSavedDelta); + compiler->unwindSaveReg(REG_RA, calleeSavedDelta); - genSaveCalleeSavedRegistersHelp(maskSaveRegsInt | maskSaveRegsFloat, genFuncletInfo.fiSP_to_PSP_slot_delta + 8, - 0); + // sd fp, #(calleeSavedDelta+8)(sp) + emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, calleeSavedDelta + 8); + compiler->unwindSaveReg(REG_FP, calleeSavedDelta + 8); } - else if (genFuncletInfo.fiFrameType == 2) + else { - // fiFrameType constraints: - assert(frameSize < -2048); - - offset = -frameSize - genFuncletInfo.fiSP_to_FPRA_save_delta; - int spDelta = roundUp((UINT)offset, STACK_ALIGN); - offset = spDelta - offset; + assert(frameSize < -2040); - genStackPointerAdjustment(-spDelta, rsGetRsvdReg(), nullptr, /* reportUnwindData */ true); + int spDelta = frameSize + calleeSavedDelta; - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, offset); - compiler->unwindSaveReg(REG_FP, offset); + // addi sp, sp, #spDelta + genStackPointerAdjustment(spDelta, REG_SCRATCH, nullptr, /* reportUnwindData */ true); - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, offset + 8); - compiler->unwindSaveReg(REG_RA, offset + 8); + genSaveCalleeSavedRegistersHelp(maskSaveRegs, genFuncletInfo.fiCalleeSavedPadding, 0); + regsSavedSize += genFuncletInfo.fiCalleeSavedPadding; - maskSaveRegsInt &= ~(RBM_RA | RBM_FP); // We've saved these now + // sd ra, #regsSavedSize(sp) + emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, regsSavedSize); + compiler->unwindSaveReg(REG_RA, regsSavedSize); - offset = frameSize + spDelta + genFuncletInfo.fiSP_to_PSP_slot_delta + 8; - genSaveCalleeSavedRegistersHelp(maskSaveRegsInt | maskSaveRegsFloat, offset, 0); + // sd fp, #(regsSavedSize+8)(sp) + emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, regsSavedSize + 8); + compiler->unwindSaveReg(REG_FP, regsSavedSize + 8); - genStackPointerAdjustment(frameSize + spDelta, rsGetRsvdReg(), nullptr, - /* reportUnwindData */ true); - } - else - { - unreached(); + // addi sp, sp -#calleeSavedDelta + genStackPointerAdjustment(-calleeSavedDelta, REG_SCRATCH, nullptr, /* reportUnwindData */ true); } // This is the end of the OS-reported prolog for purposes of unwinding @@ -638,6 +960,12 @@ void CodeGen::genFuncletProlog(BasicBlock* block) } } +/***************************************************************************** + * + * Generates code for an EH funclet epilog. + * + * See the description of frame shapes at genFuncletProlog(). + */ void CodeGen::genFuncletEpilog() { #ifdef DEBUG @@ -646,93 +974,80 @@ void CodeGen::genFuncletEpilog() printf("*************** In genFuncletEpilog()\n"); } #endif + // TODO-RISCV64: Implement varargs (NYI_RISCV64) + // TODO-RISCV64-CQ: We can use C extension for optimization ScopedSetVariable _setGeneratingEpilog(&compiler->compGeneratingEpilog, true); - bool unwindStarted = false; - int frameSize = genFuncletInfo.fiSpDelta1; + compiler->unwindBegEpilog(); - if (!unwindStarted) - { - // We can delay this until we know we'll generate an unwindable instruction, if necessary. - compiler->unwindBegEpilog(); - unwindStarted = true; - } + const int frameSize = genFuncletInfo.fiSpDelta; - regMaskTP maskRestoreRegsFloat = genFuncletInfo.fiSaveRegs & RBM_ALLFLOAT; - regMaskTP maskRestoreRegsInt = genFuncletInfo.fiSaveRegs & ~maskRestoreRegsFloat; + assert(frameSize < 0); - // Funclets must always save RA and FP, since when we have funclets we must have an FP frame. - assert((maskRestoreRegsInt & RBM_RA) != 0); - assert((maskRestoreRegsInt & RBM_FP) != 0); + regMaskTP maskRestoreRegs = genFuncletInfo.fiSaveRegs & RBM_CALLEE_SAVED; + int regsRestoreSize = (compiler->compCalleeRegsPushed - 2) << 3; -#ifdef DEBUG - if (compiler->opts.disAsm) - { - printf("DEBUG: CodeGen::genFuncletEpilog, frameType:%d\n\n", genFuncletInfo.fiFrameType); - } -#endif + int calleeSavedDelta = genFuncletInfo.fiSP_to_CalleeSaved_delta; - regMaskTP regsToRestoreMask = maskRestoreRegsInt | maskRestoreRegsFloat; + emitter* emit = GetEmitter(); + regNumber tempReg = rsGetRsvdReg(); - assert(frameSize < 0); - if (genFuncletInfo.fiFrameType == 1) + if (calleeSavedDelta + regsRestoreSize + genFuncletInfo.fiCalleeSavedPadding <= 2040) { - // fiFrameType constraints: - assert(frameSize >= -2048); - assert(genFuncletInfo.fiSP_to_FPRA_save_delta < 2040); - - regsToRestoreMask &= ~(RBM_RA | RBM_FP); // We restore FP/RA at the end - - genRestoreCalleeSavedRegistersHelp(regsToRestoreMask, genFuncletInfo.fiSP_to_PSP_slot_delta + 8, 0); + calleeSavedDelta += genFuncletInfo.fiCalleeSavedPadding; + genRestoreCalleeSavedRegistersHelp(maskRestoreRegs, calleeSavedDelta, 0); + calleeSavedDelta += regsRestoreSize; - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, genFuncletInfo.fiSP_to_FPRA_save_delta + 8); - compiler->unwindSaveReg(REG_RA, genFuncletInfo.fiSP_to_FPRA_save_delta + 8); + // ld ra, #calleeSavedDelta(sp) + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, calleeSavedDelta); + compiler->unwindSaveReg(REG_RA, calleeSavedDelta); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, genFuncletInfo.fiSP_to_FPRA_save_delta); - compiler->unwindSaveReg(REG_FP, genFuncletInfo.fiSP_to_FPRA_save_delta); + // ld fp, #(calleeSavedDelta+8)(sp) + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, calleeSavedDelta + 8); + compiler->unwindSaveReg(REG_FP, calleeSavedDelta + 8); - // generate daddiu SP,SP,imm - genStackPointerAdjustment(-frameSize, rsGetRsvdReg(), nullptr, /* reportUnwindData */ true); + // addi sp, sp, -#frameSize + genStackPointerAdjustment(-frameSize, tempReg, nullptr, /* reportUnwindData */ true); } - else if (genFuncletInfo.fiFrameType == 2) + else { - // fiFrameType constraints: - assert(frameSize < -2048); - - int offset = -frameSize - genFuncletInfo.fiSP_to_FPRA_save_delta; - int spDelta = roundUp((UINT)offset, STACK_ALIGN); - offset = spDelta - offset; - - // first, generate daddiu SP,SP,imm - genStackPointerAdjustment(-frameSize - spDelta, rsGetRsvdReg(), nullptr, - /* reportUnwindData */ true); + assert(frameSize < -2040); - int offset2 = frameSize + spDelta + genFuncletInfo.fiSP_to_PSP_slot_delta + 8; - assert(offset2 < 2040); // can amend. + // addi sp, sp, #calleeSavedDelta + genStackPointerAdjustment(calleeSavedDelta, tempReg, nullptr, /* reportUnwindData */ true); - regsToRestoreMask &= ~(RBM_RA | RBM_FP); // We restore FP/RA at the end - genRestoreCalleeSavedRegistersHelp(regsToRestoreMask, offset2, 0); + genRestoreCalleeSavedRegistersHelp(maskRestoreRegs, genFuncletInfo.fiCalleeSavedPadding, 0); + regsRestoreSize += genFuncletInfo.fiCalleeSavedPadding; - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, offset + 8); - compiler->unwindSaveReg(REG_RA, offset + 8); + // ld ra, #regsRestoreSize(sp) + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, regsRestoreSize); + compiler->unwindSaveReg(REG_RA, regsRestoreSize); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, offset); - compiler->unwindSaveReg(REG_FP, offset); + // ld fp, #(regsRestoreSize+8)(sp) + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, regsRestoreSize + 8); + compiler->unwindSaveReg(REG_FP, regsRestoreSize + 8); - // second, generate daddiu SP,SP,imm for remaine space. - genStackPointerAdjustment(spDelta, rsGetRsvdReg(), nullptr, /* reportUnwindData */ true); + // addi sp, sp, -#(frameSize + calleeSavedDelta) + genStackPointerAdjustment(-(frameSize + calleeSavedDelta), tempReg, nullptr, /* reportUnwindData */ true); } - else - { - unreached(); - } - GetEmitter()->emitIns_R_R_I(INS_jalr, emitActualTypeSize(TYP_I_IMPL), REG_R0, REG_RA, 0); + + // jarl zero, ra + emit->emitIns_R_R_I(INS_jalr, emitActualTypeSize(TYP_I_IMPL), REG_R0, REG_RA, 0); compiler->unwindReturn(REG_RA); compiler->unwindEndEpilog(); } +/***************************************************************************** + * + * Capture the information used to generate the funclet prologs and epilogs. + * Note that all funclet prologs are identical, and all funclet epilogs are + * identical (per type: filters are identical, and non-filters are identical). + * Thus, we compute the data used for these just once. + * + * See genFuncletProlog() for more information about the prolog/epilog sequences. + */ void CodeGen::genCaptureFuncletPrologEpilogInfo() { if (!compiler->ehAnyFunclets()) @@ -745,87 +1060,87 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo() // The frame size and offsets must be finalized assert(compiler->lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT); - genFuncletInfo.fiFunction_CallerSP_to_FP_delta = genCallerSPtoFPdelta(); - regMaskTP rsMaskSaveRegs = regSet.rsMaskCalleeSaved; assert((rsMaskSaveRegs & RBM_RA) != 0); assert((rsMaskSaveRegs & RBM_FP) != 0); unsigned pspSize = (compiler->lvaPSPSym != BAD_VAR_NUM) ? 8 : 0; - unsigned saveRegsCount = genCountBits(rsMaskSaveRegs); - assert((saveRegsCount == compiler->compCalleeRegsPushed) || (saveRegsCount == compiler->compCalleeRegsPushed - 1)); + // If there is a PSP slot, we have to pad the funclet frame size for OSR. + // For more details see CodeGen::genFuncletProlog + // + unsigned osrPad = 0; + if (compiler->opts.IsOSR() && (pspSize != 0)) + { + osrPad = compiler->info.compPatchpointInfo->TotalFrameSize(); - unsigned saveRegsPlusPSPSize = - roundUp((UINT)genTotalFrameSize(), STACK_ALIGN) - compiler->compLclFrameSize + pspSize; + // osrPad must be aligned to stackSize + assert(osrPad % STACK_ALIGN == 0); + } - unsigned saveRegsPlusPSPSizeAligned = roundUp(saveRegsPlusPSPSize, STACK_ALIGN); + genFuncletInfo.fiCalleeSavedPadding = 0; + genFuncletInfo.fiFunction_CallerSP_to_FP_delta = genCallerSPtoFPdelta() - osrPad; - assert(compiler->lvaOutgoingArgSpaceSize % REGSIZE_BYTES == 0); - unsigned outgoingArgSpaceAligned = roundUp(compiler->lvaOutgoingArgSpaceSize, STACK_ALIGN); + unsigned savedRegsSize = genCountBits(rsMaskSaveRegs); + assert(savedRegsSize == compiler->compCalleeRegsPushed); + savedRegsSize <<= 3; - unsigned maxFuncletFrameSizeAligned = saveRegsPlusPSPSizeAligned + outgoingArgSpaceAligned; - assert((maxFuncletFrameSizeAligned % STACK_ALIGN) == 0); + unsigned saveRegsPlusPSPSize = savedRegsSize + pspSize; - int spToFpraSaveDelta = compiler->lvaOutgoingArgSpaceSize; + assert(compiler->lvaOutgoingArgSpaceSize % REGSIZE_BYTES == 0); + unsigned outgoingArgSpaceAligned = roundUp(compiler->lvaOutgoingArgSpaceSize, STACK_ALIGN); - unsigned funcletFrameSize = saveRegsPlusPSPSize + compiler->lvaOutgoingArgSpaceSize; + unsigned funcletFrameSize = osrPad + saveRegsPlusPSPSize + compiler->lvaOutgoingArgSpaceSize; unsigned funcletFrameSizeAligned = roundUp(funcletFrameSize, STACK_ALIGN); - assert(funcletFrameSizeAligned <= maxFuncletFrameSizeAligned); - - unsigned funcletFrameAlignmentPad = funcletFrameSizeAligned - funcletFrameSize; - assert((funcletFrameAlignmentPad == 0) || (funcletFrameAlignmentPad == REGSIZE_BYTES)); - if (maxFuncletFrameSizeAligned <= (2048 - 8)) - { - genFuncletInfo.fiFrameType = 1; - saveRegsPlusPSPSize -= 2 * 8; // FP/RA - } - else + int SP_to_CalleeSaved_delta = compiler->lvaOutgoingArgSpaceSize; + if ((SP_to_CalleeSaved_delta + savedRegsSize) >= 2040) { - unsigned saveRegsPlusPSPAlignmentPad = saveRegsPlusPSPSizeAligned - saveRegsPlusPSPSize; - assert((saveRegsPlusPSPAlignmentPad == 0) || (saveRegsPlusPSPAlignmentPad == REGSIZE_BYTES)); + int offset = funcletFrameSizeAligned - SP_to_CalleeSaved_delta; + SP_to_CalleeSaved_delta = AlignUp((UINT)offset, STACK_ALIGN); - genFuncletInfo.fiFrameType = 2; - saveRegsPlusPSPSize -= 2 * 8; // FP/RA + genFuncletInfo.fiCalleeSavedPadding = SP_to_CalleeSaved_delta - offset; } - int callerSpToPspSlotDelta = -(int)saveRegsPlusPSPSize; - genFuncletInfo.fiSpDelta1 = -(int)funcletFrameSizeAligned; - int spToPspSlotDelta = funcletFrameSizeAligned - saveRegsPlusPSPSize; + if (compiler->lvaMonAcquired != BAD_VAR_NUM && !compiler->opts.IsOSR()) + { + // We furthermore allocate the "monitor acquired" bool between PSP and + // the saved registers because this is part of the EnC header. + // Note that OSR methods reuse the monitor bool created by tier 0. + osrPad += compiler->lvaLclSize(compiler->lvaMonAcquired); + } /* Now save it for future use */ - genFuncletInfo.fiSaveRegs = rsMaskSaveRegs; - genFuncletInfo.fiSP_to_FPRA_save_delta = spToFpraSaveDelta; - - genFuncletInfo.fiSP_to_PSP_slot_delta = spToPspSlotDelta; - genFuncletInfo.fiCallerSP_to_PSP_slot_delta = callerSpToPspSlotDelta; + genFuncletInfo.fiSpDelta = -(int)funcletFrameSizeAligned; + genFuncletInfo.fiSaveRegs = rsMaskSaveRegs; + genFuncletInfo.fiSP_to_CalleeSaved_delta = SP_to_CalleeSaved_delta; + genFuncletInfo.fiSP_to_PSP_slot_delta = funcletFrameSizeAligned - osrPad - 8; + genFuncletInfo.fiCallerSP_to_PSP_slot_delta = -(int)osrPad - 8; #ifdef DEBUG if (verbose) { printf("\n"); printf("Funclet prolog / epilog info\n"); - printf(" Save regs: "); + printf(" Save regs: "); dspRegMask(genFuncletInfo.fiSaveRegs); printf("\n"); - printf(" Function CallerSP-to-FP delta: %d\n", genFuncletInfo.fiFunction_CallerSP_to_FP_delta); - printf(" SP to FP/RA save location delta: %d\n", genFuncletInfo.fiSP_to_FPRA_save_delta); - printf(" Frame type: %d\n", genFuncletInfo.fiFrameType); - printf(" SP delta 1: %d\n", genFuncletInfo.fiSpDelta1); - - if (compiler->lvaPSPSym != BAD_VAR_NUM) + if (compiler->opts.IsOSR()) { - if (callerSpToPspSlotDelta != compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym)) // for - // debugging - { - printf("lvaGetCallerSPRelativeOffset(lvaPSPSym): %d\n", - compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym)); - } + printf(" OSR Pad: %d\n", osrPad); } + printf(" Function CallerSP-to-FP delta: %d\n", genFuncletInfo.fiFunction_CallerSP_to_FP_delta); + printf(" SP to CalleeSaved location delta: %d\n", genFuncletInfo.fiSP_to_CalleeSaved_delta); + printf(" SP delta: %d\n", genFuncletInfo.fiSpDelta); } + assert(genFuncletInfo.fiSP_to_CalleeSaved_delta >= 0); - assert(genFuncletInfo.fiSP_to_FPRA_save_delta >= 0); + if (compiler->lvaPSPSym != BAD_VAR_NUM) + { + assert(genFuncletInfo.fiCallerSP_to_PSP_slot_delta == + compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym)); // same offset used in main function and + // funclet! + } #endif // DEBUG } @@ -1008,14 +1323,19 @@ void CodeGen::genSetPSPSym(regNumber initReg, bool* pInitRegZeroed) noway_assert(isFramePointerUsed()); // We need an explicit frame pointer - int spToCallerSpDelta = -genCallerSPtoInitialSPdelta(); + int SPtoCallerSPdelta = -genCallerSPtoInitialSPdelta(); + + if (compiler->opts.IsOSR()) + { + SPtoCallerSPdelta += compiler->info.compPatchpointInfo->TotalFrameSize(); + } // We will just use the initReg since it is an available register // and we are probably done using it anyway... regNumber regTmp = initReg; *pInitRegZeroed = false; - genInstrWithConstant(INS_addi, EA_PTRSIZE, regTmp, REG_SPBASE, spToCallerSpDelta, rsGetRsvdReg(), false); + genInstrWithConstant(INS_addi, EA_PTRSIZE, regTmp, REG_SPBASE, SPtoCallerSPdelta, regTmp, false); GetEmitter()->emitIns_S_R(INS_sd, EA_PTRSIZE, regTmp, compiler->lvaPSPSym, 0); } @@ -1272,7 +1592,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre emitAttr size = emitActualTypeSize(tree); double constValue = tree->AsDblCon()->DconValue(); - // Make sure we use "daddiu reg, zero, 0x00" only for positive zero (0.0) + // Make sure we use "fmv.w.x reg, zero" only for positive zero (0.0) // and not for negative zero (-0.0) if (FloatingPointUtils::isPositiveZero(constValue)) { @@ -1290,12 +1610,10 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre CORINFO_FIELD_HANDLE hnd = emit->emitFltOrDblConst(constValue, size); // Load the FP constant. - assert(targetReg >= REG_F0); - - instruction ins = size == EA_4BYTE ? INS_flw : INS_fld; + assert(emit->isFloatReg(targetReg)); // Compute the address of the FP constant and load the data. - emit->emitIns_R_C(ins, size, targetReg, REG_NA, hnd, 0); + emit->emitIns_R_C(size == EA_4BYTE ? INS_flw : INS_fld, size, targetReg, REG_NA, hnd, 0); } } break; @@ -1685,12 +2003,18 @@ void CodeGen::genLclHeap(GenTree* tree) BasicBlock* endLabel = nullptr; // can optimize for riscv64. unsigned stackAdjustment = 0; const target_ssize_t ILLEGAL_LAST_TOUCH_DELTA = (target_ssize_t)-1; - target_ssize_t lastTouchDelta = - ILLEGAL_LAST_TOUCH_DELTA; // The number of bytes from SP to the last stack address probed. + + // The number of bytes from SP to the last stack address probed. + target_ssize_t lastTouchDelta = ILLEGAL_LAST_TOUCH_DELTA; noway_assert(isFramePointerUsed()); // localloc requires Frame Pointer to be established since SP changes noway_assert(genStackLevel == 0); // Can't have anything on the stack + const target_ssize_t pageSize = compiler->eeGetPageSize(); + + // According to RISC-V Privileged ISA page size is 4KiB + noway_assert(pageSize == 0x1000); + // compute the amount of memory to allocate to properly STACK_ALIGN. size_t amount = 0; if (size->IsCnsIntOrI()) @@ -1792,7 +2116,7 @@ void CodeGen::genLclHeap(GenTree* tree) goto ALLOC_DONE; } } - else if (amount < compiler->eeGetPageSize()) // must be < not <= + else if (amount < pageSize) // must be < not <= { // Since the size is less than a page, simply adjust the SP value. // The SP might already be in the guard page, so we must touch it BEFORE @@ -1882,8 +2206,6 @@ void CodeGen::genLclHeap(GenTree* tree) // addi regCnt, REG_R0, 0 // // Skip: - // sub regCnt, SP, regCnt - // // lui regTmp, eeGetPageSize()>>12 // Loop: // lw r0, 0(SP) // tickle the page - read from the page @@ -1899,20 +2221,19 @@ void CodeGen::genLclHeap(GenTree* tree) if (tempReg == REG_NA) tempReg = tree->ExtractTempReg(); - regNumber regTmp = tree->GetSingleTempReg(); + regNumber rPageSize = tree->GetSingleTempReg(); assert(regCnt != tempReg); emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, tempReg, REG_SPBASE, regCnt); - //// subu regCnt, SP, regCnt // regCnt now holds ultimate SP + // sub regCnt, SP, regCnt // regCnt now holds ultimate SP emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, regCnt, REG_SPBASE, regCnt); // Overflow, set regCnt to lowest possible value emit->emitIns_R_R_I(INS_beq, EA_PTRSIZE, tempReg, REG_R0, 2 << 2); emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, regCnt, REG_R0, 0); - assert(compiler->eeGetPageSize() == ((compiler->eeGetPageSize() >> 12) << 12)); - emit->emitIns_R_I(INS_lui, EA_PTRSIZE, regTmp, compiler->eeGetPageSize() >> 12); + emit->emitIns_R_I(INS_lui, EA_PTRSIZE, rPageSize, pageSize >> 12); // genDefineTempLabel(loop); @@ -1920,14 +2241,14 @@ void CodeGen::genLclHeap(GenTree* tree) emit->emitIns_R_R_I(INS_lw, EA_4BYTE, REG_R0, REG_SPBASE, 0); // decrement SP by eeGetPageSize() - emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, tempReg, REG_SPBASE, regTmp); + emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, tempReg, REG_SPBASE, rPageSize); - assert(regTmp != tempReg); + assert(rPageSize != tempReg); ssize_t imm = 3 << 2; // goto done. emit->emitIns_R_R_I(INS_bltu, EA_PTRSIZE, tempReg, regCnt, imm); - emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, regTmp); + emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, rPageSize); imm = -4 << 2; // Jump to loop and tickle new stack address @@ -1951,8 +2272,7 @@ void CodeGen::genLclHeap(GenTree* tree) assert((lastTouchDelta == ILLEGAL_LAST_TOUCH_DELTA) || (lastTouchDelta >= 0)); if ((lastTouchDelta == ILLEGAL_LAST_TOUCH_DELTA) || - (stackAdjustment + (unsigned)lastTouchDelta + STACK_PROBE_BOUNDARY_THRESHOLD_BYTES > - compiler->eeGetPageSize())) + (stackAdjustment + (unsigned)lastTouchDelta + STACK_PROBE_BOUNDARY_THRESHOLD_BYTES > pageSize)) { genStackPointerConstantAdjustmentLoopWithProbe(-(ssize_t)stackAdjustment, tempReg); } @@ -1969,7 +2289,7 @@ void CodeGen::genLclHeap(GenTree* tree) else // stackAdjustment == 0 { // Move the final value of SP to targetReg - GetEmitter()->emitIns_R_R_I(INS_ori, EA_PTRSIZE, targetReg, REG_SPBASE, 0); + emit->emitIns_R_R_I(INS_ori, EA_PTRSIZE, targetReg, REG_SPBASE, 0); } BAILOUT: @@ -2306,6 +2626,27 @@ void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* node) } } +// Generate code for CpObj nodes which copy structs that have interleaved +// GC pointers. +// For this case we'll generate a sequence of loads/stores in the case of struct +// slots that don't contain GC pointers. The generated code will look like: +// ld tempReg, 8(a5) +// sd tempReg, 8(a6) +// +// In the case of a GC-Pointer we'll call the ByRef write barrier helper +// who happens to use the same registers as the previous call to maintain +// the same register requirements and register killsets: +// call CORINFO_HELP_ASSIGN_BYREF +// +// So finally an example would look like this: +// ld tempReg, 8(a5) +// sd tempReg 8(a6) +// call CORINFO_HELP_ASSIGN_BYREF +// ld tempReg, 8(a5) +// sd tempReg, 8(a6) +// call CORINFO_HELP_ASSIGN_BYREF +// ld tempReg, 8(a5) +// sd tempReg, 8(a6) void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode) { GenTree* dstAddr = cpObjNode->Addr(); @@ -3499,28 +3840,7 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) { assert(targetReg != REG_NA); assert(tree->TypeGet() != TYP_VOID); - if (op1->isContainedIntOrIImmed()) - { - op1 = tree->gtOp2; - op2 = tree->gtOp1; - switch (tree->OperGet()) - { - case GT_LT: - tree->SetOper(GT_GT); - break; - case GT_LE: - tree->SetOper(GT_GE); - break; - case GT_GT: - tree->SetOper(GT_LT); - break; - case GT_GE: - tree->SetOper(GT_LE); - break; - default: - break; - } - } + assert(!op1->isContainedIntOrIImmed()); assert(tree->OperIs(GT_LT, GT_LE, GT_EQ, GT_NE, GT_GT, GT_GE)); @@ -3936,8 +4256,9 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) int CodeGenInterface::genSPtoFPdelta() const { assert(isFramePointerUsed()); + assert(compiler->compCalleeRegsPushed >= 2); - int delta = compiler->lvaOutgoingArgSpaceSize; + int delta = compiler->lvaOutgoingArgSpaceSize + (compiler->compCalleeRegsPushed << 3) - 8; assert(delta >= 0); return delta; @@ -3991,9 +4312,7 @@ int CodeGenInterface::genCallerSPtoFPdelta() const int CodeGenInterface::genCallerSPtoInitialSPdelta() const { - int callerSPtoSPdelta = 0; - - callerSPtoSPdelta -= genTotalFrameSize(); + int callerSPtoSPdelta = -genTotalFrameSize(); assert(callerSPtoSPdelta <= 0); return callerSPtoSPdelta; @@ -4831,27 +5150,34 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) return; } + if (compiler->opts.IsOSR() && compiler->info.compPatchpointInfo->HasSecurityCookie()) + { + // Security cookie is on original frame and was initialized there. + return; + } + + emitter* emit = GetEmitter(); + if (compiler->gsGlobalSecurityCookieAddr == nullptr) { noway_assert(compiler->gsGlobalSecurityCookieVal != 0); instGen_Set_Reg_To_Imm(EA_PTRSIZE, initReg, compiler->gsGlobalSecurityCookieVal); - GetEmitter()->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); + emit->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); } else { if (compiler->opts.compReloc) { - GetEmitter()->emitIns_R_AI(INS_jalr, EA_PTR_DSP_RELOC, initReg, - (ssize_t)compiler->gsGlobalSecurityCookieAddr); + emit->emitIns_R_AI(INS_jalr, EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); } else { - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, initReg, ((size_t)compiler->gsGlobalSecurityCookieAddr)); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, initReg, initReg, 0); + emit->emitLoadImmediate(EA_PTRSIZE, initReg, ((size_t)compiler->gsGlobalSecurityCookieAddr)); + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, initReg, initReg, 0); } regSet.verifyRegUsed(initReg); - GetEmitter()->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); + emit->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); } *pInitRegZeroed = false; @@ -7156,11 +7482,172 @@ void CodeGen::genEstablishFramePointer(int delta, bool reportUnwindData) } //------------------------------------------------------------------------ -// genAllocLclFrame: Probe the stack and allocate the local stack frame: subtract from SP. +// genStackProbe: Probe the stack without changing it +// +// Notes: +// This function is using loop to probe each memory page. +// +// Arguments: +// frameSize - total frame size +// rOffset - usually initial register number +// rLimit - an extra register for comparison +// rPageSize - register for storing page size +// +void CodeGen::genStackProbe(ssize_t frameSize, regNumber rOffset, regNumber rLimit, regNumber rPageSize) +{ + // make sure frameSize safely fits within 4 bytes + noway_assert((ssize_t)(int)frameSize == (ssize_t)frameSize); + + const target_size_t pageSize = compiler->eeGetPageSize(); + + // According to RISC-V Privileged ISA page size should be equal 4KiB + noway_assert(pageSize == 0x1000); + emitter* emit = GetEmitter(); + + emit->emitLoadImmediate(EA_PTRSIZE, rLimit, -frameSize); + regSet.verifyRegUsed(rLimit); + + emit->emitIns_R_R_R(INS_add, EA_PTRSIZE, rLimit, rLimit, REG_SPBASE); + + emit->emitIns_R_I(INS_lui, EA_PTRSIZE, rPageSize, pageSize >> 12); + regSet.verifyRegUsed(rPageSize); + + emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, rOffset, REG_SPBASE, rPageSize); + + // Loop: + // tickle the page - Read from the updated SP - this triggers a page fault when on the guard page + emit->emitIns_R_R_I(INS_lw, EA_4BYTE, REG_R0, rOffset, 0); + emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, rOffset, rOffset, rPageSize); + + // each instr is 4 bytes + // if (rOffset >= rLimit) goto Loop; + emit->emitIns_R_R_I(INS_bge, EA_PTRSIZE, rOffset, rLimit, -2 << 2); +} + +//------------------------------------------------------------------------ +// genAllocLclFrame: Probe the stack. +// +// Notes: +// This only does the probing; allocating the frame is done when callee-saved registers are saved. +// This is done before anything has been pushed. The previous frame might have a large outgoing argument +// space that has been allocated, but the lowest addresses have not been touched. Our frame setup might +// not touch up to the first 504 bytes. This means we could miss a guard page. On Windows, however, +// there are always three guard pages, so we will not miss them all. On Linux, there is only one guard +// page by default, so we need to be more careful. We do an extra probe if we might not have probed +// recently enough. That is, if a call and prolog establishment might lead to missing a page. We do this +// on Windows as well just to be consistent, even though it should not be necessary. +// +// Arguments: +// frameSize - the size of the stack frame being allocated. +// initReg - register to use as a scratch register. +// pInitRegZeroed - OUT parameter. *pInitRegZeroed is set to 'false' if and only if +// this call sets 'initReg' to a non-zero value. Otherwise, it is unchanged. +// maskArgRegsLiveIn - incoming argument registers that are currently live. +// +// Return value: +// None +// void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pInitRegZeroed, regMaskTP maskArgRegsLiveIn) { - NYI_RISCV64("genAllocLclFrame-----unimplemented/unused on RISCV64 yet----"); + assert(compiler->compGeneratingProlog); + + if (frameSize == 0) + { + return; + } + + // According to RISC-V Privileged ISA page size should be equal 4KiB + const target_size_t pageSize = compiler->eeGetPageSize(); + + assert(!compiler->info.compPublishStubParam || (REG_SECRET_STUB_PARAM != initReg)); + + target_size_t lastTouchDelta = 0; + + emitter* emit = GetEmitter(); + + // Emit the following sequence to 'tickle' the pages. + // Note it is important that stack pointer not change until this is complete since the tickles + // could cause a stack overflow, and we need to be able to crawl the stack afterward + // (which means the stack pointer needs to be known). + + if (frameSize < pageSize) + { + // no probe needed + lastTouchDelta = frameSize; + } + else if (frameSize < 3 * pageSize) + { + // between 1 and 3 pages we will probe each page without a loop, + // because it is faster that way and doesn't cost us much + lastTouchDelta = frameSize; + + for (target_size_t probeOffset = pageSize; probeOffset <= frameSize; probeOffset += pageSize) + { + emit->emitIns_R_I(INS_lui, EA_PTRSIZE, initReg, probeOffset >> 12); + regSet.verifyRegUsed(initReg); + + emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, initReg, REG_SPBASE, initReg); + emit->emitIns_R_R_I(INS_lw, EA_4BYTE, REG_R0, initReg, 0); + + lastTouchDelta -= pageSize; + } + + assert(pInitRegZeroed != nullptr); + *pInitRegZeroed = false; // The initReg does not contain zero + + assert(lastTouchDelta == frameSize % pageSize); + compiler->unwindPadding(); + } + else + { + // probe each page, that we need to allocate large stack frame + assert(frameSize >= 3 * pageSize); + + regMaskTP availMask = RBM_ALLINT & (regSet.rsGetModifiedRegsMask() | ~RBM_INT_CALLEE_SAVED); + availMask &= ~maskArgRegsLiveIn; // Remove all of the incoming argument registers + // as they are currently live + availMask &= ~genRegMask(initReg); // Remove the pre-calculated initReg + + noway_assert(availMask != RBM_NONE); + + regMaskTP regMask = genFindLowestBit(availMask); + regNumber rLimit = genRegNumFromMask(regMask); + + availMask &= ~regMask; // Remove rLimit register + + noway_assert(availMask != RBM_NONE); + + regMask = genFindLowestBit(availMask); + regNumber rPageSize = genRegNumFromMask(regMask); + + genStackProbe((ssize_t)frameSize, initReg, rLimit, rPageSize); + + assert(pInitRegZeroed != nullptr); + *pInitRegZeroed = false; // The initReg does not contain zero + + lastTouchDelta = frameSize % pageSize; + compiler->unwindPadding(); + } + +#if STACK_PROBE_BOUNDARY_THRESHOLD_BYTES != 0 + // if the last page was too far, we will make an extra probe at the bottom + if (lastTouchDelta + STACK_PROBE_BOUNDARY_THRESHOLD_BYTES > pageSize) + { + assert(lastTouchDelta + STACK_PROBE_BOUNDARY_THRESHOLD_BYTES < pageSize << 1); + + emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, initReg, REG_R0, frameSize); + regSet.verifyRegUsed(initReg); + + emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, initReg, REG_SPBASE, initReg); + emit->emitIns_R_R_I(INS_lw, EA_4BYTE, REG_R0, initReg, 0); + + assert(pInitRegZeroed != nullptr); + *pInitRegZeroed = false; // The initReg does not contain zero + + compiler->unwindPadding(); + } +#endif } inline void CodeGen::genJumpToThrowHlpBlk_la( @@ -7319,12 +7806,77 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) /*----------------------------------------------------------------------------- * - * Push/Pop any callee-saved registers we have used + * Push/Pop any callee-saved registers we have used, + * For most frames, generatint liking: + * addi sp, sp, -#framesz ; establish the frame + * + * ; save float regs + * fsd f8, #offset(sp) + * fsd f9, #(offset+8)(sp) + * fsd f18, #(offset+16)(sp) + * ; ... + * fsd f27, #(offset+8*11)(sp) + * + * ; save int regs + * sd s1, #offset2(sp) + * sd s2, #(offset2+8)(sp) + * ; ... + * sd s11, #(offset+8*10)(sp) + * + * ; save ra, fp + * sd ra, #offset3(sp) ; save RA (8 bytes) + * sd fp, #(offset3+8)(sp) ; save FP (8 bytes) + * + * Notes: + * 1. FP is always saved, and the first store is FP, RA. + * 2. General-purpose registers are 8 bytes, floating-point registers are 8 bytes. + * 3. For frames with varargs, not implemented completely and not tested ! + * 4. We allocate the frame here; no further changes to SP are allowed (except in the body, for localloc). + * + * For functions with GS and localloc, we change the frame so the frame pointer and RA are saved at the top + * of the frame, just under the varargs registers (if any). Note that the funclet frames must follow the same + * rule, and both main frame and funclet frames (if any) must put PSPSym in the same offset from Caller-SP. + * Since this frame type is relatively rare, we force using it via stress modes, for additional coverage. + * + * The frames look like the following (simplified to only include components that matter for establishing the + * frames). See also Compiler::lvaAssignFrameOffsets(). + * + * The RISC-V's frame layout is liking: + * + * | | + * |-----------------------| + * | incoming arguments | + * +=======================+ <---- Caller's SP + * | Arguments Or | // if needed + * | Varargs regs space | // Only for varargs functions; NYI on RV64 + * |-----------------------| + * | MonitorAcquired | // 8 bytes; for synchronized methods + * |-----------------------| + * | PSP slot | // 8 bytes (omitted in NativeAOT ABI) + * |-----------------------| + * | locals, temps, etc. | + * |-----------------------| + * | possible GS cookie | + * |-----------------------| + * | Saved FP | // 8 bytes + * |-----------------------| + * | Saved RA | // 8 bytes + * |-----------------------| + * |Callee saved registers | // not including FP/RA; multiple of 8 bytes + * |-----------------------| + * | Outgoing arg space | // multiple of 8 bytes; if required (i.e., #outsz != 0) + * |-----------------------| <---- Ambient SP + * | | | + * ~ | Stack grows ~ + * | | downward | + * V + * */ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroed) { assert(compiler->compGeneratingProlog); + // Unlike on x86/x64, we can also push float registers to stack regMaskTP rsPushRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; #if ETW_EBP_FRAMED @@ -7334,11 +7886,8 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe } #endif - // On RISCV64 we push the FP (frame-pointer) here along with all other callee saved registers - if (isFramePointerUsed()) - { - rsPushRegs |= RBM_FPBASE; - } + // On RV64 we always use the FP (frame-pointer) + assert(isFramePointerUsed()); // // It may be possible to skip pushing/popping ra for leaf methods. However, such optimization would require @@ -7360,29 +7909,25 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe // is not worth it. // - rsPushRegs |= RBM_RA; // We must save the return address (in the RA register). - regSet.rsMaskCalleeSaved = rsPushRegs; - regMaskTP maskSaveRegsFloat = rsPushRegs & RBM_ALLFLOAT; - regMaskTP maskSaveRegsInt = rsPushRegs & ~maskSaveRegsFloat; + // we will push callee-saved registers along with fp and ra registers to stack + regMaskTP rsPushRegsMask = rsPushRegs | RBM_FP | RBM_RA; + regSet.rsMaskCalleeSaved = rsPushRegsMask; #ifdef DEBUG - if (compiler->compCalleeRegsPushed != genCountBits(rsPushRegs)) + if (compiler->compCalleeRegsPushed != genCountBits(rsPushRegsMask)) { printf("Error: unexpected number of callee-saved registers to push. Expected: %d. Got: %d ", - compiler->compCalleeRegsPushed, genCountBits(rsPushRegs)); - dspRegMask(rsPushRegs); + compiler->compCalleeRegsPushed, genCountBits(rsPushRegsMask)); + dspRegMask(rsPushRegsMask); printf("\n"); - assert(compiler->compCalleeRegsPushed == genCountBits(rsPushRegs)); + assert(compiler->compCalleeRegsPushed == genCountBits(rsPushRegsMask)); } -#endif // DEBUG - - int totalFrameSize = genTotalFrameSize(); - int offset; // This will be the starting place for saving the callee-saved registers, in increasing order. - -#ifdef DEBUG if (verbose) { + regMaskTP maskSaveRegsFloat = rsPushRegs & RBM_FLT_CALLEE_SAVED; + regMaskTP maskSaveRegsInt = rsPushRegs & RBM_INT_CALLEE_SAVED; + printf("Save float regs: "); dspRegMask(maskSaveRegsFloat); printf("\n"); @@ -7400,96 +7945,70 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe // first save instruction as a "predecrement" amount, if possible. int calleeSaveSPDelta = 0; - // By default, we'll establish the frame pointer chain. (Note that currently frames without FP are NYI.) - bool establishFramePointer = true; - - // If we do establish the frame pointer, what is the amount we add to SP to do so? - unsigned offsetSpToSavedFp = 0; - - if (isFramePointerUsed()) - { - // We need to save both FP and RA. - - assert((maskSaveRegsInt & RBM_FP) != 0); - assert((maskSaveRegsInt & RBM_RA) != 0); - - // If we need to generate a GS cookie, we need to make sure the saved frame pointer and return address - // (FP and RA) are protected from buffer overrun by the GS cookie. If FP/RA are at the lowest addresses, - // then they are safe, since they are lower than any unsafe buffers. And the GS cookie we add will - // protect our caller's frame. If we have a localloc, however, that is dynamically placed lower than our - // saved FP/RA. In that case, we save FP/RA along with the rest of the callee-saved registers, above - // the GS cookie. - // - // After the frame is allocated, the frame pointer is established, pointing at the saved frame pointer to - // create a frame pointer chain. - // + // If we need to generate a GS cookie, we need to make sure the saved frame pointer and return address + // (FP and RA) are protected from buffer overrun by the GS cookie. If FP/RA are at the lowest addresses, + // then they are safe, since they are lower than any unsafe buffers. And the GS cookie we add will + // protect our caller's frame. If we have a localloc, however, that is dynamically placed lower than our + // saved FP/RA. In that case, we save FP/RA along with the rest of the callee-saved registers, above + // the GS cookie. + // + // After the frame is allocated, the frame pointer is established, pointing at the saved frame pointer to + // create a frame pointer chain. + // - if (totalFrameSize < 2048) - { - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, -totalFrameSize); - compiler->unwindAllocStack(totalFrameSize); + // This will be the starting place for saving the callee-saved registers, in increasing order. + int offset = compiler->lvaOutgoingArgSpaceSize; - // Case #1. - // - // Generate: - // addi sp, sp, -framesz - // sd fp, outsz(sp) - // sd ra, outsz+8(sp) - // - // The (totalFrameSize <= 2047) condition ensures the offsets of sd/ld. - // - // After saving callee-saved registers, we establish the frame pointer with: - // daddiu fp, sp, offset-fp - // We do this *after* saving callee-saved registers, so the prolog/epilog unwind codes mostly match. + int totalFrameSize = genTotalFrameSize(); - JITDUMP("Frame type 1. #outsz=%d; #framesz=%d; LclFrameSize=%d\n", - unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, compiler->compLclFrameSize); + emitter* emit = GetEmitter(); - frameType = 1; + // ensure offset of sd/ld + if (totalFrameSize <= 2040) + { + frameType = 1; - offsetSpToSavedFp = compiler->lvaOutgoingArgSpaceSize; + emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, -totalFrameSize); + compiler->unwindAllocStack(totalFrameSize); - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, offsetSpToSavedFp); - compiler->unwindSaveReg(REG_FP, offsetSpToSavedFp); + JITDUMP("Frame type 1. #outsz=%d; #framesz=%d; LclFrameSize=%d\n", unsigned(compiler->lvaOutgoingArgSpaceSize), + totalFrameSize, compiler->compLclFrameSize); + } + else + { + frameType = 2; + // we have to adjust stack pointer; probably using add instead of addi - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, offsetSpToSavedFp + 8); - compiler->unwindSaveReg(REG_RA, offsetSpToSavedFp + 8); + JITDUMP("Frame type 2. #outsz=%d; #framesz=%d; LclFrameSize=%d\n", unsigned(compiler->lvaOutgoingArgSpaceSize), + totalFrameSize, compiler->compLclFrameSize); - maskSaveRegsInt &= ~(RBM_FP | RBM_RA); // We've already saved FP/RA + if ((offset + (compiler->compCalleeRegsPushed << 3)) >= 2040) + { + offset = totalFrameSize - compiler->lvaOutgoingArgSpaceSize; + calleeSaveSPDelta = AlignUp((UINT)offset, STACK_ALIGN); + offset = calleeSaveSPDelta - offset; - offset = compiler->compLclFrameSize + 2 * REGSIZE_BYTES; // FP/RA + genStackPointerAdjustment(-calleeSaveSPDelta, initReg, pInitRegZeroed, /* reportUnwindData */ true); } else { - JITDUMP("Frame type 2. #outsz=%d; #framesz=%d; LclFrameSize=%d\n", - unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, compiler->compLclFrameSize); - - frameType = 2; - - maskSaveRegsInt &= ~(RBM_FP | RBM_RA); // We've already saved FP/RA - - offset = totalFrameSize - compiler->compLclFrameSize - 2 * REGSIZE_BYTES; - calleeSaveSPDelta = AlignUp((UINT)offset, STACK_ALIGN); - offset = calleeSaveSPDelta - offset; + genStackPointerAdjustment(-totalFrameSize, initReg, pInitRegZeroed, /* reportUnwindData */ true); } } - else - { - // No frame pointer (no chaining). - assert((maskSaveRegsInt & RBM_FP) == 0); - assert((maskSaveRegsInt & RBM_RA) != 0); - // Note that there is no pre-indexed save_lrpair unwind code variant, so we can't allocate the frame using - // 'sd' if we only have one callee-saved register plus RA to save. + JITDUMP(" offset=%d, calleeSaveSPDelta=%d\n", offset, calleeSaveSPDelta); - NYI_RISCV64("Frame without frame pointer"); - offset = 0; - } + genSaveCalleeSavedRegistersHelp(rsPushRegs, offset, 0); + offset += (int)(genCountBits(rsPushRegs) << 3); // each reg has 8 bytes - assert(frameType != 0); + emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, offset); + compiler->unwindSaveReg(REG_RA, offset); - JITDUMP(" offset=%d, calleeSaveSPDelta=%d\n", offset, calleeSaveSPDelta); - genSaveCalleeSavedRegistersHelp(maskSaveRegsInt | maskSaveRegsFloat, offset, -calleeSaveSPDelta); + emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, offset + 8); + compiler->unwindSaveReg(REG_FP, offset + 8); + + JITDUMP(" offsetSpToSavedFp=%d\n", offset + 8); + genEstablishFramePointer(offset + 8, /* reportUnwindData */ true); // For varargs, home the incoming arg registers last. Note that there is nothing to unwind here, // so we just report "NOP" unwind codes. If there's no more frame setup after this, we don't @@ -7506,60 +8025,12 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe printf("DEBUG: RISCV64, frameType:%d\n\n", frameType); } #endif - if (frameType == 1) - { - // offsetSpToSavedFp = genSPtoFPdelta(); - } - else if (frameType == 2) - { - if (compiler->lvaOutgoingArgSpaceSize >= 2040) - { - offset = totalFrameSize - calleeSaveSPDelta - compiler->lvaOutgoingArgSpaceSize; - calleeSaveSPDelta = AlignUp((UINT)offset, STACK_ALIGN); - offset = calleeSaveSPDelta - offset; - - genStackPointerAdjustment(-calleeSaveSPDelta, initReg, pInitRegZeroed, /* reportUnwindData */ true); - - offsetSpToSavedFp = offset; - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, offset); - compiler->unwindSaveReg(REG_FP, offset); - - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, offset + 8); - compiler->unwindSaveReg(REG_RA, offset + 8); - - genEstablishFramePointer(offset, /* reportUnwindData */ true); - - calleeSaveSPDelta = compiler->lvaOutgoingArgSpaceSize & ~0xf; - genStackPointerAdjustment(-calleeSaveSPDelta, initReg, pInitRegZeroed, /* reportUnwindData */ true); - } - else - { - calleeSaveSPDelta = totalFrameSize - calleeSaveSPDelta; - genStackPointerAdjustment(-calleeSaveSPDelta, initReg, pInitRegZeroed, /* reportUnwindData */ true); - - offset = compiler->lvaOutgoingArgSpaceSize; - - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_FP, REG_SPBASE, offset); - compiler->unwindSaveReg(REG_FP, offset); - - GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_RA, REG_SPBASE, offset + 8); - compiler->unwindSaveReg(REG_RA, offset + 8); - - genEstablishFramePointer(offset, /* reportUnwindData */ true); - } - - establishFramePointer = false; - } - else - { - unreached(); - } - - if (establishFramePointer) + if (calleeSaveSPDelta != 0) { - JITDUMP(" offsetSpToSavedFp=%d\n", offsetSpToSavedFp); - genEstablishFramePointer(offsetSpToSavedFp, /* reportUnwindData */ true); + assert(frameType == 2); + calleeSaveSPDelta = totalFrameSize - calleeSaveSPDelta; + genStackPointerAdjustment(-calleeSaveSPDelta, initReg, pInitRegZeroed, /* reportUnwindData */ true); } } @@ -7567,149 +8038,112 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) { assert(compiler->compGeneratingEpilog); - regMaskTP rsRestoreRegs = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; + regMaskTP regsToRestoreMask = regSet.rsGetModifiedRegsMask() & RBM_CALLEE_SAVED; - if (isFramePointerUsed()) - { - rsRestoreRegs |= RBM_FPBASE; - } - - rsRestoreRegs |= RBM_RA; // We must save/restore the return address. - - regMaskTP regsToRestoreMask = rsRestoreRegs; - - int totalFrameSize = genTotalFrameSize(); + // On RV64 we always use the FP (frame-pointer) + assert(isFramePointerUsed()); + int totalFrameSize = genTotalFrameSize(); + int remainingSPSize = totalFrameSize; + int callerSPtoFPdelta = 0; int calleeSaveSPOffset = 0; // This will be the starting place for restoring // the callee-saved registers, in decreasing order. - int frameType = 0; // An indicator of what type of frame we are popping. - int calleeSaveSPDelta = 0; // Amount to add to SP after callee-saved registers have been restored. - - if (isFramePointerUsed()) - { - if (totalFrameSize <= 2047) - { - if (compiler->compLocallocUsed) - { - int spToFpDelta = genSPtoFPdelta(); - // Restore sp from fp - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_FPBASE, -spToFpDelta); - compiler->unwindSetFrameReg(REG_FPBASE, spToFpDelta); - } - JITDUMP("Frame type 1(save FP/RA at bottom). #outsz=%d; #framesz=%d; localloc? %s\n", - unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, dspBool(compiler->compLocallocUsed)); - - frameType = 1; + emitter* emit = GetEmitter(); - regsToRestoreMask &= ~(RBM_FP | RBM_RA); // We'll restore FP/RA at the end. + // ensure offset of sd/ld + if (totalFrameSize <= 2040) + { + JITDUMP("Frame type 1. #outsz=%d; #framesz=%d; localloc? %s\n", unsigned(compiler->lvaOutgoingArgSpaceSize), + totalFrameSize, dspBool(compiler->compLocallocUsed)); - calleeSaveSPOffset = compiler->compLclFrameSize + 2 * REGSIZE_BYTES; - } - else + if (compiler->compLocallocUsed) { - JITDUMP("Frame type 2(save FP/RA at bottom). #outsz=%d; #framesz=%d; #calleeSaveRegsPushed:%d; " - "localloc? %s\n", - unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, compiler->compCalleeRegsPushed, - dspBool(compiler->compLocallocUsed)); + callerSPtoFPdelta = (compiler->compCalleeRegsPushed << 3) - 8 + compiler->lvaOutgoingArgSpaceSize; + } + calleeSaveSPOffset = compiler->lvaOutgoingArgSpaceSize; + // remainingSPSize = totalFrameSize; + } + else + { + JITDUMP("Frame type 2. #outsz=%d; #framesz=%d; calleeSaveRegsPushed: %d; localloc? %s\n", + unsigned(compiler->lvaOutgoingArgSpaceSize), totalFrameSize, compiler->compCalleeRegsPushed, + dspBool(compiler->compLocallocUsed)); - frameType = 2; + if ((compiler->lvaOutgoingArgSpaceSize + (compiler->compCalleeRegsPushed << 3)) >= 2040) + { + calleeSaveSPOffset = compiler->lvaOutgoingArgSpaceSize & 0xfffffff0; - int outSzAligned; - if (compiler->lvaOutgoingArgSpaceSize >= 2040) + if (compiler->compLocallocUsed) { - int offset = totalFrameSize - compiler->compLclFrameSize - 2 * REGSIZE_BYTES; - calleeSaveSPDelta = AlignUp((UINT)offset, STACK_ALIGN); - calleeSaveSPOffset = calleeSaveSPDelta - offset; - - int offset2 = totalFrameSize - calleeSaveSPDelta - compiler->lvaOutgoingArgSpaceSize; - calleeSaveSPDelta = AlignUp((UINT)offset2, STACK_ALIGN); - offset2 = calleeSaveSPDelta - offset2; - - if (compiler->compLocallocUsed) - { - // Restore sp from fp - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_FPBASE, -offset2); - compiler->unwindSetFrameReg(REG_FPBASE, offset2); - } - else - { - outSzAligned = compiler->lvaOutgoingArgSpaceSize & ~0xf; - genStackPointerAdjustment(outSzAligned, rsGetRsvdReg(), nullptr, - /* reportUnwindData */ true); - } - - regsToRestoreMask &= ~(RBM_FP | RBM_RA); // We'll restore FP/RA at the end. - - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, offset2 + 8); - compiler->unwindSaveReg(REG_RA, offset2 + 8); - - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, offset2); - compiler->unwindSaveReg(REG_FP, offset2); - - genStackPointerAdjustment(calleeSaveSPDelta, rsGetRsvdReg(), nullptr, - /* reportUnwindData */ true); - - calleeSaveSPDelta = totalFrameSize - compiler->compLclFrameSize - 2 * REGSIZE_BYTES; - calleeSaveSPDelta = AlignUp((UINT)calleeSaveSPDelta, STACK_ALIGN); + callerSPtoFPdelta = (compiler->compCalleeRegsPushed << 3) - 8; } else { - int offset2 = compiler->lvaOutgoingArgSpaceSize; - if (compiler->compLocallocUsed) - { - // Restore sp from fp - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_FPBASE, -offset2); - compiler->unwindSetFrameReg(REG_FPBASE, offset2); - } - - regsToRestoreMask &= ~(RBM_FP | RBM_RA); // We'll restore FP/RA at the end. - - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, offset2 + 8); - compiler->unwindSaveReg(REG_RA, offset2 + 8); - - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, offset2); - compiler->unwindSaveReg(REG_FP, offset2); - - calleeSaveSPOffset = totalFrameSize - compiler->compLclFrameSize - 2 * REGSIZE_BYTES; - calleeSaveSPDelta = AlignUp((UINT)calleeSaveSPOffset, STACK_ALIGN); - calleeSaveSPOffset = calleeSaveSPDelta - calleeSaveSPOffset; - - genStackPointerAdjustment(totalFrameSize - calleeSaveSPDelta, rsGetRsvdReg(), nullptr, - /* reportUnwindData */ true); + genStackPointerAdjustment(calleeSaveSPOffset, REG_RA, nullptr, /* reportUnwindData */ true); } + calleeSaveSPOffset = compiler->lvaOutgoingArgSpaceSize - calleeSaveSPOffset; + remainingSPSize = remainingSPSize - calleeSaveSPOffset; + } + else + { + if (compiler->compLocallocUsed) + { + callerSPtoFPdelta = (compiler->compCalleeRegsPushed << 3) - 8 + compiler->lvaOutgoingArgSpaceSize; + } + calleeSaveSPOffset = compiler->lvaOutgoingArgSpaceSize; + // remainingSPSize = totalFrameSize; } } - else + + if (compiler->compLocallocUsed) { - // No frame pointer (no chaining). - NYI_RISCV64("Frame without frame pointer"); - calleeSaveSPOffset = 0; + // restore sp form fp: addi sp, -#callerSPtoFPdelta(fp) + emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_FPBASE, -callerSPtoFPdelta); + compiler->unwindSetFrameReg(REG_FPBASE, callerSPtoFPdelta); } - JITDUMP(" calleeSaveSPOffset=%d, calleeSaveSPDelta=%d\n", calleeSaveSPOffset, calleeSaveSPDelta); - genRestoreCalleeSavedRegistersHelp(regsToRestoreMask, calleeSaveSPOffset, calleeSaveSPDelta); + JITDUMP(" calleeSaveSPOffset=%d, callerSPtoFPdelta=%d\n", calleeSaveSPOffset, callerSPtoFPdelta); + genRestoreCalleeSavedRegistersHelp(regsToRestoreMask, calleeSaveSPOffset, 0); - if (frameType == 1) - { - calleeSaveSPOffset = compiler->lvaOutgoingArgSpaceSize; + // restore ra/fp regs + calleeSaveSPOffset += (compiler->compCalleeRegsPushed - 2) << 3; - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, calleeSaveSPOffset + 8); - compiler->unwindSaveReg(REG_RA, calleeSaveSPOffset + 8); + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_RA, REG_SPBASE, calleeSaveSPOffset); + compiler->unwindSaveReg(REG_RA, calleeSaveSPOffset); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, calleeSaveSPOffset); - compiler->unwindSaveReg(REG_FP, calleeSaveSPOffset); + emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_FP, REG_SPBASE, calleeSaveSPOffset + 8); + compiler->unwindSaveReg(REG_FP, calleeSaveSPOffset + 8); - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, totalFrameSize); - compiler->unwindAllocStack(totalFrameSize); - } - else if (frameType == 2) + if (emitter::isValidUimm11(remainingSPSize)) { - // had done. + emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, remainingSPSize); } else { - unreached(); + regNumber tempReg = rsGetRsvdReg(); + emit->emitLoadImmediate(EA_PTRSIZE, tempReg, remainingSPSize); + emit->emitIns_R_R_R(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, tempReg); + } + compiler->unwindAllocStack(remainingSPSize); + + // for OSR we have to adjust SP to remove tier0 frame + if (compiler->opts.IsOSR()) + { + const int tier0FrameSize = compiler->info.compPatchpointInfo->TotalFrameSize(); + JITDUMP("Extra SP adjust for OSR to pop off Tier0 frame: %d bytes\n", tier0FrameSize); + + if (emitter::isValidUimm11(tier0FrameSize)) + { + emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, tier0FrameSize); + } + else + { + regNumber tempReg = rsGetRsvdReg(); + emit->emitLoadImmediate(EA_PTRSIZE, tempReg, tier0FrameSize); + emit->emitIns_R_R_R(INS_add, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, tempReg); + } + compiler->unwindAllocStack(tier0FrameSize); } } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7fed233ed8cce8..bd8cd590eea537 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5682,7 +5682,7 @@ void Compiler::generatePatchpointInfo() // const int totalFrameSize = codeGen->genTotalFrameSize() + TARGET_POINTER_SIZE; const int offsetAdjust = 0; -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // SP is not manipulated by calls so no frame size adjustment needed. // Local Offsets may need adjusting, if FP is at bottom of frame. // @@ -6947,7 +6947,7 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, { frameSizeUpdate = 8; } -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) if ((totalFrameSize & 0xf) != 0) { frameSizeUpdate = 8; diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index ad75dbf6270993..96005e6057662d 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -3888,7 +3888,7 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz // const int osrOffset = ppInfo->GenericContextArgOffset() - 2 * REGSIZE_BYTES; assert(offset == osrOffset); -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // PP info has virtual offset. This is also the caller SP offset. // const int osrOffset = ppInfo->GenericContextArgOffset(); @@ -3931,7 +3931,7 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz // const int osrOffset = ppInfo->KeptAliveThisOffset() - 2 * REGSIZE_BYTES; assert(offset == osrOffset); -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // PP info has virtual offset. This is also the caller SP offset. // const int osrOffset = ppInfo->KeptAliveThisOffset(); diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 50afc5c7cf1c06..16622e227de63b 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -574,11 +574,11 @@ CONFIG_INTEGER(JitRandomGuardedDevirtualization, W("JitRandomGuardedDevirtualiza #endif // DEBUG // Enable insertion of patchpoints into Tier0 methods, switching to optimized where needed. -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) CONFIG_INTEGER(TC_OnStackReplacement, W("TC_OnStackReplacement"), 1) #else CONFIG_INTEGER(TC_OnStackReplacement, W("TC_OnStackReplacement"), 0) -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Initial patchpoint counter value used by jitted code CONFIG_INTEGER(TC_OnStackReplacement_InitialCounter, W("TC_OnStackReplacement_InitialCounter"), 1000) // Enable partial compilation for Tier0 methods diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 0eaf5f3313c6f4..11b452493c2700 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -5229,7 +5229,7 @@ void Compiler::lvaFixVirtualFrameOffsets() if (opts.IsOSR()) { -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // Stack offset includes Tier0 frame. // JITDUMP("--- delta bump %d for OSR + Tier0 frame\n", info.compPatchpointInfo->TotalFrameSize()); @@ -5334,7 +5334,7 @@ void Compiler::lvaFixVirtualFrameOffsets() #endif // FEATURE_FIXED_OUT_ARGS -#if defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#if defined(TARGET_ARM64) // We normally add alignment below the locals between them and the outgoing // arg space area. When we store fp/lr(ra) at the bottom, however, this will // be below the alignment. So we should not apply the alignment adjustment to @@ -5346,11 +5346,11 @@ void Compiler::lvaFixVirtualFrameOffsets() { lvaTable[lvaRetAddrVar].SetStackOffset(REGSIZE_BYTES); } -#elif defined(TARGET_LOONGARCH64) +#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) assert(codeGen->isFramePointerUsed()); if (lvaRetAddrVar != BAD_VAR_NUM) { - // For LoongArch64, the RA is below the fp. see the `genPushCalleeSavedRegisters` + // For LoongArch64 and RISCV64, the RA is below the fp. see the `genPushCalleeSavedRegisters` lvaTable[lvaRetAddrVar].SetStackOffset(-REGSIZE_BYTES); } #endif // !TARGET_LOONGARCH64 @@ -6139,17 +6139,11 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() stkOffs -= (compCalleeRegsPushed - 2) * REGSIZE_BYTES; } -#elif defined(TARGET_LOONGARCH64) - - assert(compCalleeRegsPushed >= 2); - -#elif defined(TARGET_RISCV64) +#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // Subtract off FP and RA. assert(compCalleeRegsPushed >= 2); - stkOffs -= (compCalleeRegsPushed - 2) * REGSIZE_BYTES; -#else // !TARGET_RISCV64 +#else // !TARGET_LOONGARCH64 && !TARGET_RISCV64 #ifdef TARGET_ARM // On ARM32 LR is part of the pushed registers and is always stored at the // top. @@ -6160,7 +6154,7 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() #endif stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES; -#endif // !TARGET_RISCV64 +#endif // !TARGET_LOONGARCH64 && !TARGET_RISCV64 // (2) Account for the remainder of the frame // @@ -6882,11 +6876,6 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() } #endif // TARGET_ARM64 -#if defined(TARGET_RISCV64) - assert(isFramePointerUsed()); // Note that currently we always have a frame pointer - stkOffs -= 2 * REGSIZE_BYTES; -#endif // TARGET_RISCV64 - #if FEATURE_FIXED_OUT_ARGS if (lvaOutgoingArgSpaceSize > 0) { @@ -6903,8 +6892,8 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals() } #endif // FEATURE_FIXED_OUT_ARGS -#ifdef TARGET_LOONGARCH64 - // For LoongArch64, CalleeSavedRegs are at bottom. +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + // For LoongArch64 and RISCV64, CalleeSavedRegs are at bottom. int pushedCount = 0; #else // compLclFrameSize equals our negated virtual stack offset minus the pushed registers and return address @@ -7891,7 +7880,7 @@ int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased, bool forRo offset += codeGen->genCallerSPtoInitialSPdelta(); } -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) if (forRootFrame && opts.IsOSR()) { const PatchpointInfo* const ppInfo = info.compPatchpointInfo; @@ -7910,7 +7899,7 @@ int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased, bool forRo // const int adjustment = ppInfo->TotalFrameSize() + REGSIZE_BYTES; -#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) const int adjustment = ppInfo->TotalFrameSize(); #endif diff --git a/src/coreclr/jit/targetriscv64.h b/src/coreclr/jit/targetriscv64.h index a12bcc04986409..9cf0185a569351 100644 --- a/src/coreclr/jit/targetriscv64.h +++ b/src/coreclr/jit/targetriscv64.h @@ -298,6 +298,9 @@ #define B_DIST_SMALL_MAX_NEG (-4096) #define B_DIST_SMALL_MAX_POS (+4095) + // The number of bytes from the end the last probed page that must also be probed, to allow for some + // small SP adjustments without probes. If zero, then the stack pointer can point to the last byte/word + // on the stack guard page, and must be touched before any further "SUB SP". #define STACK_PROBE_BOUNDARY_THRESHOLD_BYTES 0 // clang-format on From 8726d6b940bb0cfecbcade46159a1634137d7c18 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:13:09 +0100 Subject: [PATCH 157/189] [main] Update dependencies from dotnet/arcade (#97087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/arcade build 20240116.3 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24066.3 * Update Versions.props * Make xunit.props respect different versions and assert libraries * Update dependencies from https://github.com/dotnet/arcade build 20240116.3 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24066.3 * Avoid XUNIT_SPAN unambiguous call issues * Disable obsoletion warning for xunit Delegating*Sink * Fix nullable collection test * Fix "DynamicObject_MissingProperty" null test * Remove unnecessary Extensions references * Update dependencies from xharness 9.0.0-prerelease.24066.3 * Try to fix TryCatchFinally test for real * Fix TryCatchFinally for real real * Update dependencies from https://github.com/dotnet/arcade build 20240117.4 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24067.4 * Fix TryCatchFinally for real real real * Update dependencies from https://github.com/dotnet/arcade build 20240118.1 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24068.1 * Work around Mono issue in AppDomainTests.AssemblyLoad * Update dependencies from https://github.com/dotnet/arcade build 20240118.4 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24068.4 * Update dependencies from https://github.com/dotnet/arcade build 20240119.2 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24069.2 * Update xunit.props * Update xunit.props * Update dependencies from https://github.com/dotnet/arcade build 20240119.2 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.23607.2 -> To Version 9.0.0-beta.24069.2 * Disable DisabledRuntimeMarshalling_Disabled_NativeAssemblyEnabled test See https://github.com/dotnet/runtime/issues/97272 * Disable AmbiguousImplementationException.ilproj and Pause_ro.csproj too They also fail with https://github.com/dotnet/runtime/issues/97272 * Disable Regression2_Regressions.csproj too * Revert "Disable Regression2_Regressions.csproj too" This reverts commit 4a6bdb841eafab84acd1e0e0ae5423acc893651c. * Revert "Disable AmbiguousImplementationException.ilproj and Pause_ro.csproj too" This reverts commit f5729ce8fde51ebce389f3e2d505119cdfda7096. * Revert "Disable DisabledRuntimeMarshalling_Disabled_NativeAssemblyEnabled test" This reverts commit 2267a5ccdf1d2c9cddd81d2175bc403b643fe89d. * Work around IsPrimitive compilation issue --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Viktor Hofer Co-authored-by: Alexander Köplinger Co-authored-by: Michal Strehovský --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 92 +++++++++---------- eng/Versions.props | 40 ++++---- eng/common/cross/build-rootfs.sh | 16 ++-- eng/testing/xunit/xunit.props | 11 ++- global.json | 6 +- .../SingleFileTestRunner.cs | 3 + .../tests/SerialPort/ReadLine.cs | 2 +- .../tests/SerialPort/ReadTo.cs | 2 +- .../System.Memory/tests/Memory/CopyTo.cs | 2 +- .../tests/ReadOnlyMemory/CopyTo.cs | 2 +- .../tests/ReadOnlySpan/CopyTo.cs | 2 +- .../System.Memory/tests/Span/Clear.cs | 4 +- .../System.Memory/tests/Span/CopyTo.cs | 2 +- .../System.Memory/tests/Span/Fill.cs | 2 +- .../System.Memory/tests/Span/Reverse.cs | 6 +- .../System.Private.CoreLib/src/System/Type.cs | 10 +- .../tests/src/Tests/Assembly/AssemblyTests.cs | 24 ++--- .../Tests/Type/TypeTests.Get.CornerCases.cs | 4 +- .../Canonicalization/Directory.Build.props | 10 ++ ....Serialization.Xml.Canonicalization.csproj | 1 - .../tests/DataContractSerializer.cs | 6 +- ...rmance.dynamic.context.indexer.genclass.cs | 4 +- .../System/AppDomainTests.cs | 7 +- .../Reflection/TypeTests.Get.CornerCases.cs | 2 +- .../System/StringComparerTests.cs | 4 +- .../tests/EnvelopedCms/GeneralTests.cs | 2 +- ...stomConverterTests.Dynamic.Sample.Tests.cs | 2 +- .../PrecompiledRegexScenarioTest.cs | 8 +- .../wasm/symbolicator/WasmSymbolicator.csproj | 2 + 30 files changed, 157 insertions(+), 123 deletions(-) create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/Directory.Build.props diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ce102f3f63e097..b161f32faf492d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "9.0.0-prerelease.23611.1", + "version": "9.0.0-prerelease.24066.3", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7cef0e251bb632..c4af0ef4940ddf 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -111,82 +111,82 @@ - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 https://github.com/dotnet/runtime-assets @@ -321,21 +321,21 @@ https://github.com/dotnet/runtime 6f9d6569684cc17015aa6fc5f9d9a5f7580ade97 - + https://github.com/dotnet/xharness - a855ee8bf3e90a5baef9de25ccff5be597390087 + fa3b643cc0b028ae875546b7527927adcfcd9348 - + https://github.com/dotnet/xharness - a855ee8bf3e90a5baef9de25ccff5be597390087 + fa3b643cc0b028ae875546b7527927adcfcd9348 - + https://github.com/dotnet/xharness - a855ee8bf3e90a5baef9de25ccff5be597390087 + fa3b643cc0b028ae875546b7527927adcfcd9348 - + https://github.com/dotnet/arcade - 3faeb9817f465151aa4bbcdb315f0a6170206760 + abddd0bd5145578246dcadda264c7557f2a935a9 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index 9ababb1f455110..c19121336f3b19 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -82,22 +82,24 @@ 9.0.100-alpha.1.23551.3 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 2.5.3-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 - 9.0.0-beta.23607.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 2.6.7-beta.24069.2 + 9.0.0-beta.24069.2 + 2.6.7-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + 9.0.0-beta.24069.2 + + 1.4.0 6.0.0-preview.1.102 @@ -179,9 +181,9 @@ 1.4.0 17.4.0-preview-20220707-01 - 9.0.0-prerelease.23611.1 - 9.0.0-prerelease.23611.1 - 9.0.0-prerelease.23611.1 + 9.0.0-prerelease.24066.3 + 9.0.0-prerelease.24066.3 + 9.0.0-prerelease.24066.3 9.0.0-alpha.0.24065.1 3.12.0 4.1.0 diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 9b2791cf568aae..9fa764e78b0434 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -8,7 +8,7 @@ usage() echo "BuildArch can be: arm(default), arm64, armel, armv6, ppc64le, riscv64, s390x, x64, x86" echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine" echo " for alpine can be specified with version: alpineX.YY or alpineedge" - echo " for FreeBSD can be: freebsd12, freebsd13" + echo " for FreeBSD can be: freebsd13, freebsd14" echo " for illumos can be: illumos" echo " for Haiku can be: haiku." echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD" @@ -71,9 +71,9 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="12.4-RELEASE" -__FreeBSDPkg="1.17.0" -__FreeBSDABI="12" +__FreeBSDBase="13.2-RELEASE" +__FreeBSDPkg="1.20.0" +__FreeBSDABI="13" __FreeBSDPackages="libunwind" __FreeBSDPackages+=" icu" __FreeBSDPackages+=" libinotify" @@ -334,14 +334,14 @@ while :; do __AlpineVersion="$__AlpineMajorVersion.$__AlpineMinoVersion" fi ;; - freebsd12) + freebsd13) __CodeName=freebsd __SkipUnmount=1 ;; - freebsd13) + freebsd14) __CodeName=freebsd - __FreeBSDBase="13.2-RELEASE" - __FreeBSDABI="13" + __FreeBSDBase="14.0-RELEASE" + __FreeBSDABI="14" __SkipUnmount=1 ;; illumos) diff --git a/eng/testing/xunit/xunit.props b/eng/testing/xunit/xunit.props index 64286ff55ea100..457c4f402bf78b 100644 --- a/eng/testing/xunit/xunit.props +++ b/eng/testing/xunit/xunit.props @@ -6,12 +6,15 @@ - - - - + + + + + + + + + true + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj index 9e332f12609925..a07418861d50d8 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj @@ -67,7 +67,6 @@ - \ No newline at end of file diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index a7ffda68b113aa..8a7acf0800596e 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -1211,9 +1211,9 @@ public static void DCS_JaggedArrayAsRoot() string[][] jaggedStringArray = new string[][] { new string[] { "1", "3", "5", "7", "9" }, new string[] { "0", "2", "4", "6" }, new string[] { "11", "22" } }; var actualJaggedStringArray = DataContractSerializerHelper.SerializeAndDeserialize(jaggedStringArray, @"1357902461122"); - Assert.Equal(jaggedStringArray[0], actualJaggedStringArray[0]); - Assert.Equal(jaggedStringArray[1], actualJaggedStringArray[1]); - Assert.Equal(jaggedStringArray[2], actualJaggedStringArray[2]); + Assert.Equal(jaggedStringArray[0], actualJaggedStringArray[0]); + Assert.Equal(jaggedStringArray[1], actualJaggedStringArray[1]); + Assert.Equal(jaggedStringArray[2], actualJaggedStringArray[2]); object[] objectArray = new object[] { 1, 1.0F, 1.0, "string", Guid.Parse("2054fd3e-e118-476a-9962-1a882be51860"), new DateTime(2013, 1, 2) }; var actualObjectArray = DataContractSerializerHelper.SerializeAndDeserialize(objectArray, @"111string2054fd3e-e118-476a-9962-1a882be518602013-01-02T00:00:00"); diff --git a/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs b/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs index 75c76c34c69615..ffbae381f5ad97 100644 --- a/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs +++ b/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.indexer.genclass.cs @@ -801,7 +801,9 @@ public static void TryCatchFinally() } Assert.True(threwException); - Assert.Equal(new int?[] { null, null, null }, result); + int?[] resultIntegers = result; + // Assert.Equal doesn't allow to build collections with nulls in anymore. Use Assert.Collection instead. + Assert.Collection(resultIntegers, (a) => Assert.Null(a), (b) => Assert.Null(b), (c) => Assert.Null(c)); Assert.Equal(1, MemberClass.t_status); } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/AppDomainTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/AppDomainTests.cs index f473d1dec82762..1ef1c6557a749d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/AppDomainTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/AppDomainTests.cs @@ -512,9 +512,14 @@ public void AssemblyLoad() { RemoteExecutor.Invoke(() => { bool AssemblyLoadFlag = false; + bool isMono = PlatformDetection.IsMonoRuntime; AssemblyLoadEventHandler handler = (sender, args) => { - Assert.Equal(AppDomain.CurrentDomain, sender); + if (isMono) + Assert.True(AppDomain.CurrentDomain == sender); + else + Assert.Equal(AppDomain.CurrentDomain, sender); + Assert.NotNull(args); Assert.NotNull(args.LoadedAssembly); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/TypeTests.Get.CornerCases.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/TypeTests.Get.CornerCases.cs index 67c99cce94312e..5adab84fcc60e4 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/TypeTests.Get.CornerCases.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/TypeTests.Get.CornerCases.cs @@ -244,7 +244,7 @@ public static void GetPropertyHidesPropertiesByNameAndSigAndCallingConventionCom names.Sort(); string[] expected = { "Item", nameof(Base.MyInstanceThenStaticProp), nameof(Base.MyStaticThenInstanceProp), nameof(Base.MyStringThenDoubleProp) }; - Assert.Equal(expected, names.ToArray()); + Assert.Equal(expected, names.ToArray()); } private abstract class Base diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs index 3027c870855b5d..35514e9c381fac 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringComparerTests.cs @@ -68,10 +68,10 @@ public void Compare_ViaSort_SortsAsExpected() string[] strings = new[] { "a", "b", "AB", "A", "cde", "abc", "f", "123", "ab" }; Array.Sort(strings, StringComparer.OrdinalIgnoreCase); - Assert.Equal(strings, new[] { "123", "a", "A", "AB", "ab", "abc", "b", "cde", "f" }); + Assert.Equal(strings, new[] { "123", "a", "A", "AB", "ab", "abc", "b", "cde", "f" }); Array.Sort(strings, StringComparer.Ordinal); - Assert.Equal(strings, new[] { "123", "A", "AB", "a", "ab", "abc", "b", "cde", "f" }); + Assert.Equal(strings, new[] { "123", "A", "AB", "a", "ab", "abc", "b", "cde", "f" }); } [Fact] diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/GeneralTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/GeneralTests.cs index a9d532d6722f5f..2d4c1b8ac21931 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/GeneralTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/GeneralTests.cs @@ -132,7 +132,7 @@ private static void VerifyRecipients3(byte[] encodedMessage) col.CopyTo(recipients, 0); string[] actualIssuers = recipients.Select(r => r.RecipientIdentifier.Value).Cast().Select(xis => xis.IssuerName).OrderBy(s => s).ToArray(); - Assert.Equal(expectedIssuers, actualIssuers); + Assert.Equal(expectedIssuers, actualIssuers); } [Fact] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Dynamic.Sample.Tests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Dynamic.Sample.Tests.cs index 3e0f7c2de9b758..69c53e91e37d37 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Dynamic.Sample.Tests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Dynamic.Sample.Tests.cs @@ -314,7 +314,7 @@ public static void DynamicObject_MissingProperty() dynamic obj = JsonSerializer.Deserialize("{}", options); // We return null here; ExpandoObject throws for missing properties. - Assert.Equal(null, obj.NonExistingProperty); + Assert.Null(obj.NonExistingProperty); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs index a22c493303b632..264abb8ef567d0 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/PrecompiledRegexScenarioTest.cs @@ -70,8 +70,8 @@ public void PrecompiledRegex_SplitTest() { RegexTestClass testClass = new RegexTestClass(); - Assert.Equal(["", "4", "success", "\n", "5", "success", "\n", "6", "success", "\n", "7", "success", ""], testClass.Split(textWithMultipleMatches)); - Assert.Equal(["", "4", "success", $"\nbsdf135success1245somethingelse{Environment.NewLine}csdf136success2245somethingnew{Environment.NewLine}dsdf137success3245somethingold"], testClass.Split(textWithMultipleMatches, 2)); + Assert.Equal(["", "4", "success", "\n", "5", "success", "\n", "6", "success", "\n", "7", "success", ""], testClass.Split(textWithMultipleMatches)); + Assert.Equal(["", "4", "success", $"\nbsdf135success1245somethingelse{Environment.NewLine}csdf136success2245somethingnew{Environment.NewLine}dsdf137success3245somethingold"], testClass.Split(textWithMultipleMatches, 2)); } [Fact] @@ -97,8 +97,8 @@ public void PrecompiledRegex_Groups() RegexTestClass testClass = new RegexTestClass(); Assert.Equal(text, testClass.Match(text).Groups[0].Value); - Assert.Equal([0, 1, 2], testClass.GetGroupNumbers()); - Assert.Equal(["0", "1", "output"], testClass.GetGroupNames()); + Assert.Equal(new int[] { 0, 1, 2 }, testClass.GetGroupNumbers()); + Assert.Equal(["0", "1", "output"], testClass.GetGroupNames()); } } } diff --git a/src/mono/wasm/symbolicator/WasmSymbolicator.csproj b/src/mono/wasm/symbolicator/WasmSymbolicator.csproj index 7b715fc9e3fbdf..6d290842a3080e 100644 --- a/src/mono/wasm/symbolicator/WasmSymbolicator.csproj +++ b/src/mono/wasm/symbolicator/WasmSymbolicator.csproj @@ -9,7 +9,9 @@ + + From c818c214dfabf7ce31b2e79baf43cc26c82b9c69 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:33:54 +0100 Subject: [PATCH 158/189] [main] Update dependencies from dotnet/emsdk (#97098) * Update dependencies from https://github.com/dotnet/emsdk build 20240116.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24065.1 -> To Version 9.0.0-alpha.1.24066.1 * Update dependencies from https://github.com/dotnet/emsdk build 20240117.2 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24065.1 -> To Version 9.0.0-alpha.1.24067.2 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.24060.4 -> To Version 16.0.5-alpha.1.24065.3 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240118.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24065.1 -> To Version 9.0.0-alpha.1.24068.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.24060.4 -> To Version 16.0.5-alpha.1.24065.3 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240118.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24065.1 -> To Version 9.0.0-alpha.1.24068.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.24060.4 -> To Version 16.0.5-alpha.1.24065.3 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport * Update dependencies from https://github.com/dotnet/emsdk build 20240118.1 Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-alpha.1.24065.1 -> To Version 9.0.0-alpha.1.24068.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.24060.4 -> To Version 16.0.5-alpha.1.24065.3 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 128 ++++++++++++++++++++-------------------- eng/Versions.props | 64 ++++++++++---------- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c4af0ef4940ddf..cddc828e1356db 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -12,73 +12,73 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/emsdk - c14a1d2af9d67eec272ff7d7f3c5bb6b114798fe + fd99e59a43cfd6fe323c98ff267b620a67e71e67 - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e https://github.com/dotnet/command-line-api @@ -94,9 +94,9 @@ b8c2293cd1cbd9d0fe6f32d7b5befbd526b5a175 - + https://github.com/dotnet/emsdk - c14a1d2af9d67eec272ff7d7f3c5bb6b114798fe + fd99e59a43cfd6fe323c98ff267b620a67e71e67 @@ -240,61 +240,61 @@ https://github.com/dotnet/runtime-assets 6d74e6e23d77cca399e7efb223e64ae2d4fd9472 - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e - + https://github.com/dotnet/llvm-project - d8bacb4031b1d1e290ab2792e8378560419ee0de + 5de288617749cb4778fd40eadc6f2bb7a2b66a5e https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index c19121336f3b19..46236a7571f274 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -108,14 +108,14 @@ 9.0.0-alpha.1.24064.3 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 6.0.0 1.1.1 @@ -215,39 +215,39 @@ 2.2.3 9.0.0-alpha.1.24067.1 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 - 9.0.0-alpha.1.24065.1 + 9.0.0-alpha.1.24068.1 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) - 9.0.0-alpha.1.24065.1 + 9.0.0-alpha.1.24068.1 1.1.87-gba258badda 1.0.0-v3.14.0.5722 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 - 16.0.5-alpha.1.24060.4 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 + 16.0.5-alpha.1.24065.3 3.1.7 1.0.406601 From 81791d1bd0ab353c63b9a9e25cf5b789d89d7475 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:34:38 +0100 Subject: [PATCH 159/189] [main] Update dependencies from dotnet/installer (#96896) * Update dependencies from https://github.com/dotnet/installer build 20240112.1 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24062.1 * Update dependencies from https://github.com/dotnet/installer build 20240112.11 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24062.11 * Update dependencies from https://github.com/dotnet/installer build 20240112.11 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24062.11 * Update dependencies from https://github.com/dotnet/installer build 20240112.11 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24062.11 * Update dependencies from https://github.com/dotnet/installer build 20240115.1 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24065.1 * Update dependencies from https://github.com/dotnet/installer build 20240117.2 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24067.2 * Update dependencies from https://github.com/dotnet/installer build 20240117.14 Microsoft.Dotnet.Sdk.Internal From Version 9.0.100-alpha.1.24061.3 -> To Version 9.0.100-alpha.1.24067.14 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index cddc828e1356db..9c67b6ccb1f667 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -401,9 +401,9 @@ https://github.com/NuGet/NuGet.Client 8fef55f5a55a3b4f2c96cd1a9b5ddc51d4b927f8 - + https://github.com/dotnet/installer - cc07296328b45ea6721d1c17662c3bc59aaff392 + 4f7697a2dad24125e30b7e655bf5e8cfcfd61641 diff --git a/eng/Versions.props b/eng/Versions.props index 46236a7571f274..290d0b0acb8dad 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -252,7 +252,7 @@ 3.1.7 1.0.406601 - 9.0.100-alpha.1.24061.3 + 9.0.100-alpha.1.24067.14 $(MicrosoftDotnetSdkInternalVersion) From 52a9a87ece6cfd07c718b0b50537cecab861c77c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:38:27 +0100 Subject: [PATCH 160/189] [main] Update dependencies from dotnet/roslyn-analyzers (#96763) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240109.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24059.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240109.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24059.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240111.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24061.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240111.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24061.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240111.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24061.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240111.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24061.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240115.3 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24065.3 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240116.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24066.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240117.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24067.1 * Fix new CA1859 analyzer warnings * Fix more CA1859 * don't use var * Revert CA1869 changes in ILCompiler.Compiler, ILCompiler.TypeSystem projects and disable warning * Remove pragma disable in ConfigurationElement * Fix more warnings * Fix build * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240117.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24067.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240117.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24067.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240117.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24052.1 -> To Version 3.11.0-beta1.24067.1 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- .../src/System/Reflection/RuntimeCustomAttributeData.cs | 2 ++ .../src/Internal/TypeSystem/RuntimeMethodDesc.cs | 2 +- .../Compiler/Dataflow/MethodBodyScanner.cs | 4 ++-- .../aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj | 1 + .../ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | 4 ++-- .../ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj | 1 + .../managed/Microsoft.NET.HostModel/Bundle/Bundler.cs | 2 +- .../gen/LoggerMessageGenerator.Parser.cs | 2 +- .../Microsoft.Extensions.Options/gen/Generator.cs | 2 ++ .../src/System/CodeDom/Compiler/CodeGeneratorOptions.cs | 2 +- .../System/Collections/Immutable/ImmutableExtensions.cs | 2 ++ .../System/ComponentModel/PropertyDescriptorCollection.cs | 2 +- .../src/System/Configuration/ClientSettingsStore.cs | 4 ++-- .../src/System/Configuration/ConfigurationElement.cs | 2 +- .../src/System/Configuration/LocalFileSettingsProvider.cs | 4 ++-- .../src/System/Data/Odbc/OdbcMetaDataFactory.cs | 2 -- .../src/System/Diagnostics/Process.Windows.cs | 3 ++- .../ActiveDirectory/ActiveDirectorySchemaClass.cs | 4 ++-- .../Parallel/QueryOperators/Binary/ExceptQueryOperator.cs | 2 ++ .../Parallel/QueryOperators/Binary/UnionQueryOperator.cs | 2 ++ .../QueryOperators/Unary/DistinctQueryOperator.cs | 2 ++ .../src/System/Net/Http/Metrics/MetricsHandler.cs | 2 ++ .../src/System/Diagnostics/Tracing/EventProvider.cs | 2 +- .../src/System/Runtime/Serialization/DataContract.cs | 2 +- .../src/System/Runtime/Serialization/SchemaExporter.cs | 2 +- .../src/System/Xml/Serialization/Compilation.cs | 4 ++-- .../src/System/Xml/Xsl/Xslt/QilGenerator.cs | 2 +- .../System/Runtime/Serialization/Schema/CodeExporter.cs | 2 +- .../Pal/Windows/DecryptorPalWindows.DecodeRecipients.cs | 2 -- .../Cryptography/X509Certificates/ChainPal.Apple.cs | 2 +- .../System.Speech/src/Internal/SrgsCompiler/BackEnd.cs | 2 +- .../src/Recognition/SrgsGrammar/SrgsGrammarCompiler.cs | 2 +- src/libraries/System.Speech/src/Result/SemanticValue.cs | 2 +- .../System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.cs | 2 ++ .../debugger/BrowserDebugProxy/MemberObjectsExplorer.cs | 2 +- src/tasks/AotCompilerTask/MonoAOTCompiler.cs | 6 +++--- src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs | 4 ++-- src/tasks/WorkloadBuildTasks/PackageInstaller.cs | 2 +- .../src/ILLink.Tasks/CreateRuntimeRootDescriptorFile.cs | 6 +++--- .../src/analyzer/LinkerAnalyzerCore/SpaceAnalyzer.cs | 4 ++-- .../src/linker/Linker.Dataflow/MethodBodyScanner.cs | 4 ++-- 43 files changed, 67 insertions(+), 52 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9c67b6ccb1f667..6617c4cd4c33b7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -374,13 +374,13 @@ https://github.com/dotnet/roslyn 28e49407a6e4744819bd471707259b99964e441c - + https://github.com/dotnet/roslyn-analyzers - 8a92037d28baf58560622cb4685ecefdc828c2c8 + 23ec029b47d68b1a80348c0dabc3bccf013c1fe6 - + https://github.com/dotnet/roslyn-analyzers - 8a92037d28baf58560622cb4685ecefdc828c2c8 + 23ec029b47d68b1a80348c0dabc3bccf013c1fe6 https://github.com/dotnet/sdk diff --git a/eng/Versions.props b/eng/Versions.props index 290d0b0acb8dad..0faa216df17caf 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -31,8 +31,8 @@ - 3.11.0-beta1.24052.1 - 9.0.0-preview.24052.1 + 3.11.0-beta1.24067.1 + 9.0.0-preview.24067.1 diff --git a/eng/Versions.props b/eng/Versions.props index 0faa216df17caf..42de1573491ce8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -156,12 +156,12 @@ 9.0.0-beta.24068.1 9.0.0-beta.24068.1 - 1.0.0-prerelease.24056.4 - 1.0.0-prerelease.24056.4 - 1.0.0-prerelease.24056.4 - 1.0.0-prerelease.24056.4 - 1.0.0-prerelease.24056.4 - 1.0.0-prerelease.24056.4 + 1.0.0-prerelease.24063.4 + 1.0.0-prerelease.24063.4 + 1.0.0-prerelease.24063.4 + 1.0.0-prerelease.24063.4 + 1.0.0-prerelease.24063.4 + 1.0.0-prerelease.24063.4 16.11.29-beta1.23404.4 2.0.0-beta4.23407.1 From 440632c1ff56d0b6b091898c89e3e722da9565c8 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 22 Jan 2024 02:46:01 -0800 Subject: [PATCH 162/189] Don't disable crossgen in crossbuild verticals (#97163) --- src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj | 1 - src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj b/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj index 838bd77c133097..e601d1c14cf2d0 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj @@ -20,7 +20,6 @@ $(ROOTFS_DIR) true true - false true true diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj b/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj index b1c2a1835bfaab..08998d0a9bcc72 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj +++ b/src/coreclr/tools/aot/crossgen2/crossgen2_publish.csproj @@ -29,7 +29,6 @@ false false - false $(PublishReadyToRun) From ebd5cb686e1463cb927f4e969f9b5ff05adbbcc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 22 Jan 2024 13:53:50 +0100 Subject: [PATCH 163/189] Disable failing System.Numerics.Tensors tests (#97301) https://github.com/dotnet/runtime/issues/97297 https://github.com/dotnet/runtime/issues/97295 https://github.com/dotnet/runtime/issues/97296 Caused by https://github.com/dotnet/runtime/pull/97192 --- .../Common/tests/TestUtilities/System/PlatformDetection.cs | 1 + .../System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs | 4 ++++ .../System.Numerics.Tensors/tests/TensorPrimitivesTests.cs | 1 + src/libraries/tests.proj | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 68ec070f3b1d56..054a868c25f85d 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -30,6 +30,7 @@ public static partial class PlatformDetection public static bool IsMonoRuntime => Type.GetType("Mono.RuntimeStructs") != null; public static bool IsNotMonoRuntime => !IsMonoRuntime; public static bool IsMonoInterpreter => GetIsRunningOnMonoInterpreter(); + public static bool IsNotMonoInterpreter => !IsMonoInterpreter; public static bool IsMonoAOT => Environment.GetEnvironmentVariable("MONO_AOT_MODE") == "aot"; public static bool IsNotMonoAOT => Environment.GetEnvironmentVariable("MONO_AOT_MODE") != "aot"; public static bool IsNativeAot => IsNotMonoRuntime && !IsReflectionEmitSupported; diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs index 65c45de4c42556..e626337624e5fd 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs @@ -174,6 +174,7 @@ public static IEnumerable SpanDestinationFunctionsToTest() [Theory] [MemberData(nameof(SpanDestinationFunctionsToTest))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97297")] public void SpanDestinationFunctions_AllLengths(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) { Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => @@ -192,6 +193,7 @@ public void SpanDestinationFunctions_AllLengths(SpanDestinationDelegate tensorPr [Theory] [MemberData(nameof(SpanDestinationFunctionsToTest))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97297")] public void SpanDestinationFunctions_InPlace(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) { Assert.All(Helpers.TensorLengthsIncluding0, tensorLength => @@ -210,6 +212,7 @@ public void SpanDestinationFunctions_InPlace(SpanDestinationDelegate tensorPrimi [Theory] [MemberData(nameof(SpanDestinationFunctionsToTest))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97297")] public void SpanDestinationFunctions_SpecialValues(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) { Assert.All(Helpers.TensorLengths, tensorLength => @@ -230,6 +233,7 @@ public void SpanDestinationFunctions_SpecialValues(SpanDestinationDelegate tenso [Theory] [MemberData(nameof(SpanDestinationFunctionsToTest))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97297")] public void SpanDestinationFunctions_ValueRange(SpanDestinationDelegate tensorPrimitivesMethod, Func expectedMethod) { Assert.All(VectorLengthAndIteratedRange(ConvertFromSingle(-100f), ConvertFromSingle(100f), ConvertFromSingle(3f)), arg => diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs index 8a9f9ff6b4de29..def5b5cc8f1dce 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs @@ -11,6 +11,7 @@ namespace System.Numerics.Tensors.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/97295", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsNotMonoInterpreter))] public abstract class TensorPrimitivesTests where T : unmanaged, IEquatable { #region Abstract Methods Under Test diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 2595b93e91b4b8..2d3d56e60caefd 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -148,6 +148,10 @@ + + + + From 300aa0b11454eaae6a6284edb4eb5af8e91cbbfc Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 22 Jan 2024 08:28:01 -0500 Subject: [PATCH 164/189] Fix XML comment summary on TensorPrimitives.RootN (#97284) Copy/paste error --- .../src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs index fdf8e37fb06e58..2558eb16bc4af1 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs @@ -2006,7 +2006,7 @@ public static void ReciprocalSqrtEstimate(ReadOnlySpan x, Span destinat where T : IFloatingPointIeee754 => InvokeSpanIntoSpan>(x, destination); - /// Computes the element-wise rotation right of numbers in the specified tensor by the specified rotation amount. + /// Computes the element-wise n-th root of the values in the specified tensor. /// The tensor, represented as a span. /// The destination tensor, represented as a span. /// The degree of the root to be computed, represented as a scalar. From adb048b864d35e03f82152b75f7de60f6fd0d919 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 22 Jan 2024 08:30:51 -0500 Subject: [PATCH 165/189] Improve temporary TensorPrimitives.Round a bit (#97283) * Improve temporary TensorPrimitives.Round a bit - Validate arguments - Vectorize easy cases * Address PR feedback --- .../src/Resources/Strings.resx | 3 ++ .../Tensors/netcore/TensorPrimitives.T.cs | 34 ++++++++++++++++--- .../tests/TensorPrimitives.Generic.cs | 15 ++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx b/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx index def219f3544820..890bfd7ed85030 100644 --- a/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx +++ b/src/libraries/System.Numerics.Tensors/src/Resources/Strings.resx @@ -132,4 +132,7 @@ Negating the minimum value of a twos complement number is invalid. + + The value '{0}' is not valid for this usage of the type {1}. + diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs index 2558eb16bc4af1..4009257b22d36f 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.T.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace System.Numerics.Tensors { /// Performs primitive tensor operations over spans of memory. @@ -2140,6 +2137,8 @@ public static void Round(ReadOnlySpan x, int digits, Span destination) /// The destination tensor, represented as a span. /// Destination is too short. /// and reference overlapping memory locations and do not begin at the same location. + /// is invalid. + /// is invalid. /// /// /// This method effectively computes [i] = T.Round([i], , ). @@ -2148,14 +2147,41 @@ public static void Round(ReadOnlySpan x, int digits, Span destination) public static void Round(ReadOnlySpan x, int digits, MidpointRounding mode, Span destination) where T : IFloatingPoint { + if (digits == 0) + { + switch (mode) + { + case MidpointRounding.ToZero: + Truncate(x, destination); + return; + + case MidpointRounding.ToNegativeInfinity: + Floor(x, destination); + return; + + case MidpointRounding.ToPositiveInfinity: + Ceiling(x, destination); + return; + + case MidpointRounding.AwayFromZero: + case MidpointRounding.ToEven: + // TODO: Vectorize the remaining modes + break; + } + } + if (x.Length > destination.Length) { ThrowHelper.ThrowArgument_DestinationTooShort(); } + if ((uint)mode > (uint)MidpointRounding.ToPositiveInfinity) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, typeof(MidpointRounding)), nameof(mode)); + } + ValidateInputOutputSpanNonOverlapping(x, destination); - // TODO: Vectorize for (int i = 0; i < x.Length; i++) { destination[i] = T.Round(x[i], digits, mode); diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs index e626337624e5fd..4a262007abca18 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs @@ -1069,6 +1069,21 @@ public void Round_AllLengths(MidpointRounding mode, int digits) } }); } + + [Fact] + public void Round_InvalidMode_Throws() + { + T[] x = new T[10]; + AssertExtensions.Throws("mode", () => TensorPrimitives.Round(x.AsSpan(), (MidpointRounding)(-1), x.AsSpan())); + AssertExtensions.Throws("mode", () => TensorPrimitives.Round(x.AsSpan(), (MidpointRounding)5, x.AsSpan())); + } + + [Fact] + public void Round_InvalidDigits_Throws() + { + T[] x = new T[10]; + AssertExtensions.Throws("digits", () => TensorPrimitives.Round(x.AsSpan(), -1, x.AsSpan())); + } #endregion } From 4a8010d266e0d0bd2060abe6210d2c0669404d61 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 08:32:56 -0500 Subject: [PATCH 166/189] [main] Update dependencies from dotnet/msquic, dotnet/sdk (#97289) * Update dependencies from https://github.com/dotnet/sdk build 20240122.1 Microsoft.DotNet.ApiCompat.Task From Version 9.0.100-alpha.1.23551.3 -> To Version 9.0.100-alpha.1.24072.1 * Update dependencies from https://github.com/dotnet/msquic build 20240122.1 System.Net.MsQuic.Transport From Version 9.0.0-alpha.1.24067.1 -> To Version 9.0.0-alpha.1.24072.1 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b3086caf369e9c..9d1dcac02fc538 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,9 +4,9 @@ https://github.com/dotnet/icu 48829dd3ba47887db7fae2cd5f90cfc255883135 - + https://github.com/dotnet/msquic - 3fb2583170384341dbbc444cd5bb3d2319433fb6 + 805a0120a0e7a0283d6a4546d551037bc3974d29 https://github.com/dotnet/wcf @@ -382,9 +382,9 @@ https://github.com/dotnet/roslyn-analyzers 23ec029b47d68b1a80348c0dabc3bccf013c1fe6 - + https://github.com/dotnet/sdk - c518bc5c80dbf7d9944e9489237938f57fe145fd + eb47a3cf296746d8624124b5c002db9dc81bbc44 diff --git a/eng/Versions.props b/eng/Versions.props index 42de1573491ce8..97282a1c938902 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -80,7 +80,7 @@ 0.2.0 - 9.0.100-alpha.1.23551.3 + 9.0.100-alpha.1.24072.1 9.0.0-beta.24069.2 9.0.0-beta.24069.2 @@ -213,7 +213,7 @@ 9.0.0-alpha.1.24068.1 2.2.3 - 9.0.0-alpha.1.24067.1 + 9.0.0-alpha.1.24072.1 16.0.5-alpha.1.24065.3 16.0.5-alpha.1.24065.3 From d00587db256d5684ba7458225c35a06347b487eb Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Mon, 22 Jan 2024 14:35:18 +0100 Subject: [PATCH 167/189] Late cast expansion: castclass (#97237) --- src/coreclr/jit/helperexpansion.cpp | 30 ++++++++++- src/coreclr/jit/importer.cpp | 78 +---------------------------- 2 files changed, 30 insertions(+), 78 deletions(-) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index ad3b20bc7ecc6f..61fda463937895 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -1888,9 +1888,8 @@ static CORINFO_CLASS_HANDLE PickLikelyClass(Compiler* comp, IL_OFFSET offset, un // bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call) { - if (!call->IsHelperCall() || !impIsCastHelperMayHaveProfileData(call->GetHelperNum())) + if (!call->IsHelperCall()) { - // Not a cast helper we're interested in return false; } @@ -1901,6 +1900,26 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, return false; } + bool isInstanceOf = false; + switch (call->GetHelperNum()) + { + case CORINFO_HELP_ISINSTANCEOFINTERFACE: + case CORINFO_HELP_ISINSTANCEOFARRAY: + case CORINFO_HELP_ISINSTANCEOFCLASS: + case CORINFO_HELP_ISINSTANCEOFANY: + isInstanceOf = true; + break; + + case CORINFO_HELP_CHKCASTINTERFACE: + case CORINFO_HELP_CHKCASTARRAY: + case CORINFO_HELP_CHKCASTCLASS: + case CORINFO_HELP_CHKCASTANY: + break; + + default: + return false; + } + // Helper calls are never tail calls assert(!call->IsTailCall()); @@ -1946,6 +1965,13 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, return false; } + if ((castResult == TypeCompareState::MustNot) && !isInstanceOf) + { + // Don't expand castclass if likelyclass always fails the type check + // it's going to throw an exception anyway. + return false; + } + if ((info.compCompHnd->getClassAttribs(likelyCls) & (CORINFO_FLG_INTERFACE | CORINFO_FLG_ABSTRACT)) != 0) { // Possible scenario: someone changed Foo to be an interface, diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 84e402a85b0ce8..701e0463367b89 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -5518,79 +5518,6 @@ GenTree* Compiler::impCastClassOrIsInstToTree( // If the class is exact, the jit can expand the IsInst check inline. canExpandInline = isClassExact; } - - // Check if this cast helper have some profile data - // "isinst" with profile data is moved to a late phase. - // The long-term plan is to move all non-trivial expansions there. - if (impIsCastHelperMayHaveProfileData(helper) && isCastClass) - { - const int maxLikelyClasses = 32; - LikelyClassMethodRecord likelyClasses[maxLikelyClasses]; - unsigned likelyClassCount = - getLikelyClasses(likelyClasses, maxLikelyClasses, fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset); - - if (likelyClassCount > 0) - { -#ifdef DEBUG - for (UINT32 i = 0; i < likelyClassCount; i++) - { - const char* className = eeGetClassName((CORINFO_CLASS_HANDLE)likelyClasses[i].handle); - JITDUMP(" %u) %p (%s) [likelihood:%u%%]\n", i + 1, likelyClasses[i].handle, className, - likelyClasses[i].likelihood); - } - - // Optional stress mode to pick a random known class, rather than - // the most likely known class. - if (JitConfig.JitRandomGuardedDevirtualization() != 0) - { - // Reuse the random inliner's random state. - CLRRandom* const random = - impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization()); - - unsigned index = static_cast(random->Next(static_cast(likelyClassCount))); - likelyClasses[0].handle = likelyClasses[index].handle; - likelyClasses[0].likelihood = 100; - likelyClassCount = 1; - } -#endif - - LikelyClassMethodRecord likelyClass = likelyClasses[0]; - CORINFO_CLASS_HANDLE likelyCls = (CORINFO_CLASS_HANDLE)likelyClass.handle; - - // if there is a dominating candidate with >= 40% likelihood, use it - const unsigned likelihoodMinThreshold = 40; - if ((likelyCls != NO_CLASS_HANDLE) && (likelyClass.likelihood > likelihoodMinThreshold)) - { - TypeCompareState castResult = - info.compCompHnd->compareTypesForCast(likelyCls, pResolvedToken->hClass); - - // If case of MustNot we still can optimize isinst (only), e.g.: - // - // bool objIsDisposable = obj is IDisposable; - // - // when the profile tells us that obj is mostly Int32, hence, never implements that interface. - // for castclass it makes little sense as it will always throw a cast exception anyway. - if ((castResult == TypeCompareState::Must) || - (castResult == TypeCompareState::MustNot && !isCastClass)) - { - bool isAbstract = (info.compCompHnd->getClassAttribs(likelyCls) & - (CORINFO_FLG_INTERFACE | CORINFO_FLG_ABSTRACT)) != 0; - // If it's abstract it means we most likely deal with a stale PGO data so bail out. - if (!isAbstract) - { - JITDUMP("Adding \"is %s (%X)\" check as a fast path for %s using PGO data.\n", - eeGetClassName(likelyCls), likelyCls, isCastClass ? "castclass" : "isinst"); - - reversedMTCheck = castResult == TypeCompareState::MustNot; - canExpandInline = true; - partialExpand = true; - exactCls = likelyCls; - fastPathLikelihood = likelyClass.likelihood; - } - } - } - } - } } const bool expandInline = canExpandInline && shouldExpandInline; @@ -5621,10 +5548,9 @@ GenTree* Compiler::impCastClassOrIsInstToTree( compCurBB->SetFlags(BBF_HAS_HISTOGRAM_PROFILE); } } - else if (!isCastClass && impIsCastHelperMayHaveProfileData(helper)) + else if (impIsCastHelperMayHaveProfileData(helper)) { - // Maybe the late-cast-expand phase will have a better luck expanding this cast. - // TODO: enable for cast-class as well. + // Leave a note for fgLateCastExpand to expand this helper call call->gtCallMoreFlags |= GTF_CALL_M_CAST_CAN_BE_EXPANDED; call->gtCastHelperILOffset = ilOffset; } From 27da749495834694d601d43b654ef9d98fe78d5a Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Mon, 22 Jan 2024 15:00:21 +0100 Subject: [PATCH 168/189] Revert "[main] Update dependencies from dotnet/msquic, dotnet/sdk" (#97311) --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9d1dcac02fc538..b3086caf369e9c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,9 +4,9 @@ https://github.com/dotnet/icu 48829dd3ba47887db7fae2cd5f90cfc255883135 - + https://github.com/dotnet/msquic - 805a0120a0e7a0283d6a4546d551037bc3974d29 + 3fb2583170384341dbbc444cd5bb3d2319433fb6 https://github.com/dotnet/wcf @@ -382,9 +382,9 @@ https://github.com/dotnet/roslyn-analyzers 23ec029b47d68b1a80348c0dabc3bccf013c1fe6 - + https://github.com/dotnet/sdk - eb47a3cf296746d8624124b5c002db9dc81bbc44 + c518bc5c80dbf7d9944e9489237938f57fe145fd diff --git a/eng/Versions.props b/eng/Versions.props index 97282a1c938902..42de1573491ce8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -80,7 +80,7 @@ 0.2.0 - 9.0.100-alpha.1.24072.1 + 9.0.100-alpha.1.23551.3 9.0.0-beta.24069.2 9.0.0-beta.24069.2 @@ -213,7 +213,7 @@ 9.0.0-alpha.1.24068.1 2.2.3 - 9.0.0-alpha.1.24072.1 + 9.0.0-alpha.1.24067.1 16.0.5-alpha.1.24065.3 16.0.5-alpha.1.24065.3 From 86f25ad9bd314d5ffaca2c4aa18c56347497aaa7 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Mon, 22 Jan 2024 08:02:53 -0600 Subject: [PATCH 169/189] Avoid deadlocks in TypeDescriptor (#97236) --- .../System/ComponentModel/TypeDescriptor.cs | 12 ++-- .../tests/TypeDescriptorTests.cs | 58 +++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) 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 6043b69b4f80f5..948e3f4b386f35 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -33,7 +33,6 @@ public sealed class TypeDescriptor // A value of `null` indicates initialization is in progress. // A value of s_initializedDefaultProvider indicates the provider is initialized. private static readonly object s_initializedDefaultProvider = new object(); - private static readonly object s_defaultProviderSyncObject = new object(); private static WeakHashtable? s_associationTable; private static int s_metadataVersion; // a version stamp for our metadata. Used by property descriptors to know when to rebuild attributes. @@ -271,7 +270,10 @@ private static void CheckDefaultProvider(Type type) return; } - lock (s_defaultProviderSyncObject) + // Lock on s_providerTable even though s_providerTable is not modified here. + // Using a single lock prevents deadlocks since other methods that call into or are called + // by this method also lock on s_providerTable and the ordering of the locks may be different. + lock (s_providerTable) { AddDefaultProvider(type); } @@ -279,7 +281,7 @@ private static void CheckDefaultProvider(Type type) /// /// Add the default provider, if it exists. - /// For threading, this is always called under a 'lock (s_defaultProviderSyncObject)'. + /// For threading, this is always called under a 'lock (s_providerTable)'. /// private static void AddDefaultProvider(Type type) { @@ -326,11 +328,11 @@ private static void AddDefaultProvider(Type type) } /// - /// The CreateAssocation method creates an association between two objects. + /// The CreateAssociation method creates an association between two objects. /// Once an association is created, a designer or other filtering mechanism /// can add properties that route to either object into the primary object's /// property set. When a property invocation is made against the primary - /// object, GetAssocation will be called to resolve the actual object + /// object, GetAssociation will be called to resolve the actual object /// instance that is related to its type parameter. /// [EditorBrowsable(EditorBrowsableState.Advanced)] diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index 95bac7b0ba892d..b5468a6583f464 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; using Moq; using Xunit; @@ -1289,6 +1290,62 @@ public void ConcurrentGetProperties_ReturnsExpected() } } + [SkipOnPlatform(TestPlatforms.Browser, "Thread.Start is not supported on browsers.")] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void ConcurrentAddProviderAndGetProvider() + { + // Use a timeout value lower than RemoteExecutor in order to get a nice Fail message. + const int Timeout = 50000; + + RemoteInvokeOptions options = new() + { + TimeOut = 60000 + }; + + RemoteExecutor.Invoke(() => + { + using var finished = new CountdownEvent(2); + + Thread t1 = new Thread(() => + { + ConcurrentAddProvider(); + finished.Signal(); + }); + + Thread t2 = new Thread(() => + { + ConcurrentGetProvider(); + finished.Signal(); + }); + + t1.Start(); + t2.Start(); + finished.Wait(Timeout); + + if (finished.CurrentCount != 0) + { + Assert.Fail("Timeout. Possible deadlock."); + } + }, options).Dispose(); + + static void ConcurrentAddProvider() + { + var provider = new EmptyPropertiesTypeProvider(); + TypeDescriptor.AddProvider(provider, typeof(MyClass)); + + // This test primarily verifies no deadlock, but verify the values anyway. + Assert.True(TypeDescriptor.GetProvider(typeof(MyClass)).IsSupportedType(typeof(MyClass))); + } + + static void ConcurrentGetProvider() + { + TypeDescriptionProvider provider = TypeDescriptor.GetProvider(typeof(TypeWithProperty)); + + // This test primarily verifies no deadlock, but verify the values anyway. + Assert.True(provider.IsSupportedType(typeof(TypeWithProperty))); + } + } + public sealed class EmptyPropertiesTypeProvider : TypeDescriptionProvider { private sealed class EmptyPropertyListDescriptor : ICustomTypeDescriptor @@ -1343,6 +1400,7 @@ public async void GetConverter_ByMultithread_ReturnsExpected(Type typeForGetConv TypeConverter[] actualConverters = await Task.WhenAll( Enumerable.Range(0, 100).Select(_ => Task.Run(() => TypeDescriptor.GetConverter(typeForGetConverter)))); + Assert.All(actualConverters, currentConverter => Assert.IsType(expectedConverterType, currentConverter)); } From 8442ff69feaf8fed72c02b9e5ed6bcfe8bc0df05 Mon Sep 17 00:00:00 2001 From: Nikola Milosavljevic Date: Mon, 22 Jan 2024 07:53:23 -0800 Subject: [PATCH 170/189] Define installer-owned directories (#97183) --- .../Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props index b41247e3c2759d..0717c3c5a3e108 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props @@ -62,6 +62,7 @@ + From fd45f3b23adbf84fdc6d368e59ada2ad90ee08cc Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 22 Jan 2024 11:33:29 -0500 Subject: [PATCH 171/189] Add TextWriter.CreateBroadcasting (#96732) * Add TextWriter.CreateBroadcasting * Address PR feedback --- .../System.Private.CoreLib.Shared.projitems | 4 +- .../IO/TextWriter.CreateBroadcasting.cs | 520 ++++++++++++++++++ .../System.Runtime/ref/System.Runtime.cs | 1 + .../TextWriter/TextWriterTests.cs | 217 ++++++++ 4 files changed, 740 insertions(+), 2 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs 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 e166a7c85a09d0..e46bd04f7d6958 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 @@ -4,7 +4,6 @@ c5ed3c1d-b572-46f1-8f96-522a85ce1179 System.Private.CoreLib.Strings true - true @@ -524,6 +523,7 @@ + @@ -2744,4 +2744,4 @@ - + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs new file mode 100644 index 00000000000000..41fdf7eb56f964 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs @@ -0,0 +1,520 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO +{ + public abstract partial class TextWriter + { + /// + /// Creates an instance of that writes supplied inputs to each of the writers in . + /// + /// The instances to which all operations should be broadcast (multiplexed). + /// + /// An instance of that writes supplied inputs to each of the writers in + /// + /// is or it contains a . + /// + /// + /// The resulting instance will delegate each operation to each of the writers in . + /// For example, calling will write the specified char to each writer, one after the + /// other. The writers will be written to in the same order as they are specified in . + /// An exception from the operation on one writer will prevent the operation from being performed on subsequent writers. + /// + /// + /// and will return the corresponding object from first writer + /// in . + /// + /// + public static TextWriter CreateBroadcasting(params TextWriter[] writers) + { + ArgumentNullException.ThrowIfNull(writers); + + return writers.Length != 0 ? + new BroadcastingTextWriter([.. writers]) : + Null; + } + + private sealed class BroadcastingTextWriter : TextWriter + { + private readonly TextWriter[] _writers; + + public BroadcastingTextWriter(TextWriter[] writers) + { + Debug.Assert(writers is { Length: > 0 }); + foreach (TextWriter writer in writers) + { + ArgumentNullException.ThrowIfNull(writer, nameof(writers)); + } + + _writers = writers; + } + + public override Encoding Encoding => _writers[0].Encoding; + + public override IFormatProvider FormatProvider => _writers[0].FormatProvider; + + [AllowNull] + public override string NewLine + { + get => base.NewLine; + set + { + base.NewLine = value; + foreach (TextWriter writer in _writers) + { + writer.NewLine = value; + } + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + foreach (TextWriter writer in _writers) + { + writer.Dispose(); + } + } + } + + public override async ValueTask DisposeAsync() + { + foreach (TextWriter writer in _writers) + { + await writer.DisposeAsync().ConfigureAwait(false); + } + } + + public override void Flush() + { + foreach (TextWriter writer in _writers) + { + writer.Flush(); + } + } + + public override async Task FlushAsync() + { + foreach (TextWriter writer in _writers) + { + await writer.FlushAsync().ConfigureAwait(false); + } + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + foreach (TextWriter writer in _writers) + { + await writer.FlushAsync(cancellationToken).ConfigureAwait(false); + } + } + + public override void Write(bool value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(char value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(char[] buffer, int index, int count) + { + foreach (TextWriter writer in _writers) + { + writer.Write(buffer, index, count); + } + } + + public override void Write(char[]? buffer) + { + foreach (TextWriter writer in _writers) + { + writer.Write(buffer); + } + } + + public override void Write(decimal value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(double value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(int value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(long value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(ReadOnlySpan buffer) + { + foreach (TextWriter writer in _writers) + { + writer.Write(buffer); + } + } + + public override void Write(uint value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(ulong value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(float value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(string? value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(object? value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write(StringBuilder? value) + { + foreach (TextWriter writer in _writers) + { + writer.Write(value); + } + } + + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) + { + foreach (TextWriter writer in _writers) + { + writer.Write(format, arg0); + } + } + + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) + { + foreach (TextWriter writer in _writers) + { + writer.Write(format, arg0, arg1); + } + } + + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) + { + foreach (TextWriter writer in _writers) + { + writer.Write(format, arg0, arg1, arg2); + } + } + + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) + { + foreach (TextWriter writer in _writers) + { + writer.Write(format, arg); + } + } + + public override void WriteLine() + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(); + } + } + + public override void WriteLine(char value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(char[]? buffer) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(buffer); + } + } + + public override void WriteLine(char[] buffer, int index, int count) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(buffer, index, count); + } + } + + public override void WriteLine(ReadOnlySpan buffer) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(buffer); + } + } + + public override void WriteLine(bool value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(int value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(uint value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(long value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(ulong value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(float value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(double value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(decimal value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(string? value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(StringBuilder? value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine(object? value) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(value); + } + } + + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(format, arg0); + } + } + + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(format, arg0, arg1); + } + } + + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(format, arg0, arg1, arg2); + } + } + + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(format, arg); + } + } + + public override async Task WriteAsync(char value) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteAsync(value).ConfigureAwait(false); + } + } + + public override async Task WriteAsync(string? value) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteAsync(value).ConfigureAwait(false); + } + } + + public override async Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteAsync(value, cancellationToken).ConfigureAwait(false); + } + } + + public override async Task WriteAsync(char[] buffer, int index, int count) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteAsync(buffer, index, count).ConfigureAwait(false); + } + } + + public override async Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteAsync(buffer, cancellationToken).ConfigureAwait(false); + } + } + + public override async Task WriteLineAsync(char value) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteLineAsync(value).ConfigureAwait(false); + } + } + + public override async Task WriteLineAsync(string? value) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteLineAsync(value).ConfigureAwait(false); + } + } + + public override async Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteLineAsync(value, cancellationToken).ConfigureAwait(false); + } + } + + public override async Task WriteLineAsync(char[] buffer, int index, int count) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteLineAsync(buffer, index, count).ConfigureAwait(false); + } + } + + public override async Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + foreach (TextWriter writer in _writers) + { + await writer.WriteLineAsync(buffer, cancellationToken).ConfigureAwait(false); + } + } + + public override async Task WriteLineAsync() + { + foreach (TextWriter writer in _writers) + { + await writer.WriteLineAsync().ConfigureAwait(false); + } + } + } + } +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 0349694ed70241..acd4825aa99fc6 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10388,6 +10388,7 @@ protected TextWriter(System.IFormatProvider? formatProvider) { } public virtual System.IFormatProvider FormatProvider { get { throw null; } } [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public virtual string NewLine { get { throw null; } set { } } + public static System.IO.TextWriter CreateBroadcasting(params System.IO.TextWriter[] writers) { throw null; } public virtual void Close() { } public void Dispose() { } protected virtual void Dispose(bool disposing) { } 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 5515b984b8266a..d4030961fbfd80 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 @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.CodeDom.Compiler; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -685,6 +688,220 @@ public async Task FlushAsync_Precanceled() Assert.Equal(cts.Token, (await Assert.ThrowsAnyAsync(() => t)).CancellationToken); } + [Fact] + public void CreateBroadcasting_InvalidInputs_Throws() + { + AssertExtensions.Throws("writers", () => TextWriter.CreateBroadcasting(null)); + AssertExtensions.Throws("writers", () => TextWriter.CreateBroadcasting([null])); + AssertExtensions.Throws("writers", () => TextWriter.CreateBroadcasting([new StringWriter(), null])); + AssertExtensions.Throws("writers", () => TextWriter.CreateBroadcasting([null, new StringWriter()])); + } + + [Fact] + public void CreateBroadcasting_DefersToFirstWriterForProperties() + { + using TextWriter writer1 = new IndentedTextWriter(TextWriter.Null, " "); + using TextWriter writer2 = new StreamWriter(Stream.Null, Encoding.UTF32); + + Assert.Same(CultureInfo.InvariantCulture, TextWriter.CreateBroadcasting(writer1, writer2).FormatProvider); + Assert.Same(Encoding.UTF32, TextWriter.CreateBroadcasting(writer2, writer1).Encoding); + } + + [Fact] + public async Task CreateBroadcasting_DelegatesToAllWriters() + { + Assert.Same(TextWriter.Null, TextWriter.CreateBroadcasting()); + + using StringWriter sw1 = new(), sw2 = new(), oracle = new(); + using TextWriter broadcasting = TextWriter.CreateBroadcasting(sw1, TextWriter.Null, sw2); + + oracle.Write(true); + broadcasting.Write(true); + + oracle.Write('a'); + broadcasting.Write('a'); + + oracle.Write((char[])null); + broadcasting.Write((char[])null); + oracle.Write(new char[] { 'b', 'c' }); + broadcasting.Write(new char[] { 'b', 'c' }); + + oracle.Write(42m); + broadcasting.Write(42m); + + oracle.Write(43d); + broadcasting.Write(43d); + + oracle.Write(44f); + broadcasting.Write(44f); + + oracle.Write(45); + broadcasting.Write(45); + + oracle.Write(46L); + broadcasting.Write(46L); + + oracle.Write(DayOfWeek.Monday); + broadcasting.Write(DayOfWeek.Monday); + + oracle.Write((string)null); + broadcasting.Write((string)null); + oracle.Write("Tuesday"); + broadcasting.Write("Tuesday"); + + oracle.Write((StringBuilder)null); + broadcasting.Write((StringBuilder)null); + oracle.Write(new StringBuilder("Wednesday")); + broadcasting.Write(new StringBuilder("Wednesday")); + + oracle.Write(47u); + broadcasting.Write(47u); + + oracle.Write(48ul); + broadcasting.Write(48ul); + + oracle.Write("Thursday".AsSpan()); + broadcasting.Write("Thursday".AsSpan()); + + oracle.Write(" {0} ", "Friday"); + broadcasting.Write(" {0} ", "Friday"); + + oracle.Write(" {0}{1} ", "Saturday", "Sunday"); + broadcasting.Write(" {0}{1} ", "Saturday", "Sunday"); + + oracle.Write(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); + broadcasting.Write(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); + + oracle.Write(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + broadcasting.Write(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + + oracle.WriteLine(); + broadcasting.WriteLine(); + + oracle.WriteLine(true); + broadcasting.WriteLine(true); + + oracle.WriteLine('a'); + broadcasting.WriteLine('a'); + + oracle.WriteLine((char[])null); + broadcasting.WriteLine((char[])null); + oracle.WriteLine(new char[] { 'b', 'c' }); + broadcasting.WriteLine(new char[] { 'b', 'c' }); + + oracle.WriteLine(42m); + broadcasting.WriteLine(42m); + + oracle.WriteLine(43d); + broadcasting.WriteLine(43d); + + oracle.WriteLine(44f); + broadcasting.WriteLine(44f); + + oracle.WriteLine(45); + broadcasting.WriteLine(45); + + oracle.WriteLine(46L); + broadcasting.WriteLine(46L); + + oracle.WriteLine(DayOfWeek.Monday); + broadcasting.WriteLine(DayOfWeek.Monday); + + oracle.WriteLine((string)null); + broadcasting.WriteLine((string)null); + oracle.WriteLine("Tuesday"); + broadcasting.WriteLine("Tuesday"); + + oracle.WriteLine((StringBuilder)null); + broadcasting.WriteLine((StringBuilder)null); + oracle.WriteLine(new StringBuilder("Wednesday")); + broadcasting.WriteLine(new StringBuilder("Wednesday")); + + oracle.WriteLine(47u); + broadcasting.WriteLine(47u); + + oracle.WriteLine(48ul); + broadcasting.WriteLine(48ul); + + oracle.WriteLine("Thursday".AsSpan()); + broadcasting.WriteLine("Thursday".AsSpan()); + + oracle.WriteLine(" {0} ", "Friday"); + broadcasting.WriteLine(" {0} ", "Friday"); + + oracle.WriteLine(" {0}{1} ", "Saturday", "Sunday"); + broadcasting.WriteLine(" {0}{1} ", "Saturday", "Sunday"); + + oracle.WriteLine(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); + broadcasting.WriteLine(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); + + oracle.WriteLine(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + broadcasting.WriteLine(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + + await oracle.WriteAsync('a'); + await broadcasting.WriteAsync('a'); + + await oracle.WriteAsync((char[])null); + await broadcasting.WriteAsync((char[])null); + await oracle.WriteAsync(new char[] { 'b', 'c' }); + await broadcasting.WriteAsync(new char[] { 'b', 'c' }); + + await oracle.WriteAsync((string)null); + await broadcasting.WriteAsync((string)null); + await oracle.WriteAsync("Tuesday"); + await broadcasting.WriteAsync("Tuesday"); + + await oracle.WriteAsync((StringBuilder)null); + await broadcasting.WriteAsync((StringBuilder)null); + await oracle.WriteAsync(new StringBuilder("Wednesday")); + await broadcasting.WriteAsync(new StringBuilder("Wednesday")); + + await oracle.WriteLineAsync(); + await broadcasting.WriteLineAsync(); + + await oracle.WriteLineAsync('a'); + await broadcasting.WriteLineAsync('a'); + + await oracle.WriteLineAsync((char[])null); + await broadcasting.WriteLineAsync((char[])null); + await oracle.WriteLineAsync(new char[] { 'b', 'c' }); + await broadcasting.WriteLineAsync(new char[] { 'b', 'c' }); + + await oracle.WriteLineAsync((string)null); + await broadcasting.WriteLineAsync((string)null); + await oracle.WriteLineAsync("Tuesday"); + await broadcasting.WriteLineAsync("Tuesday"); + + await oracle.WriteLineAsync((StringBuilder)null); + await broadcasting.WriteLineAsync((StringBuilder)null); + await oracle.WriteLineAsync(new StringBuilder("Wednesday")); + await broadcasting.WriteLineAsync(new StringBuilder("Wednesday")); + + string expected = oracle.ToString(); + Assert.Equal(expected, sw1.ToString()); + Assert.Equal(expected, sw2.ToString()); + } + + [Fact] + public void CreateBroadcasting_AllMethodsOverridden() + { + HashSet exempted = ["Close", "Dispose", "get_NewLine", "set_NewLine"]; + + HashSet baseMethods = + typeof(TextWriter) + .GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance) + .Where(m => m.IsVirtual) + .Where(m => !exempted.Contains(m.Name)) + .ToHashSet(); + + foreach (MethodInfo derivedMethod in TextWriter.CreateBroadcasting(TextWriter.Null, TextWriter.Null).GetType().GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance)) + { + baseMethods.Remove(derivedMethod.GetBaseDefinition()); + } + + Assert.Empty(baseMethods); + } + private sealed class TrackingTextWriter : TextWriter { public bool NonCancelableFlushAsyncCalled; From 48ae7e11727d0d2c77e5468cc5aa631816b7eadc Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 22 Jan 2024 17:46:39 +0100 Subject: [PATCH 172/189] [browser] bump benchmarks to Net9 (#97305) Co-authored-by: Radek Doulik --- src/mono/sample/wasm/blazor-frame/blazor.csproj | 7 ++++--- src/mono/sample/wasm/browser-frame/browser-frame.csproj | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mono/sample/wasm/blazor-frame/blazor.csproj b/src/mono/sample/wasm/blazor-frame/blazor.csproj index 6037cd1c3c5a56..5592718e07144e 100644 --- a/src/mono/sample/wasm/blazor-frame/blazor.csproj +++ b/src/mono/sample/wasm/blazor-frame/blazor.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable blazor-template/ @@ -10,8 +10,9 @@ - - + + + diff --git a/src/mono/sample/wasm/browser-frame/browser-frame.csproj b/src/mono/sample/wasm/browser-frame/browser-frame.csproj index a7be6ddcb486b4..82eb634016a64e 100644 --- a/src/mono/sample/wasm/browser-frame/browser-frame.csproj +++ b/src/mono/sample/wasm/browser-frame/browser-frame.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 true true From 769f9ba5267291cbffadc1d6ada558f432f62888 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Mon, 22 Jan 2024 18:40:46 +0100 Subject: [PATCH 173/189] Make SourceBuild.props work on Windows as well (#97291) Changes are from https://github.com/dotnet/dotnet/pull/46 --- eng/SourceBuild.props | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/eng/SourceBuild.props b/eng/SourceBuild.props index e625b534e4f3be..3b544528694635 100644 --- a/eng/SourceBuild.props +++ b/eng/SourceBuild.props @@ -4,11 +4,9 @@ runtime - - - - ./build.sh + .\build.cmd + ./build.sh <_hostRid>$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier) @@ -30,27 +28,29 @@ - $(InnerBuildArgs) --arch $(TargetArch) - $(InnerBuildArgs) --cross - $(InnerBuildArgs) --configuration $(Configuration) - $(InnerBuildArgs) --allconfigurations - $(InnerBuildArgs) --verbosity $(LogVerbosity) - $(InnerBuildArgs) --nodereuse false - $(InnerBuildArgs) --warnAsError false - $(InnerBuildArgs) --outputrid $(TargetRid) + $(InnerBuildArgs) $(FlagParameterPrefix)arch $(TargetArch) + $(InnerBuildArgs) $(FlagParameterPrefix)cross + $(InnerBuildArgs) $(FlagParameterPrefix)configuration $(Configuration) + $(InnerBuildArgs) $(FlagParameterPrefix)allconfigurations + $(InnerBuildArgs) $(FlagParameterPrefix)verbosity $(LogVerbosity) + $(InnerBuildArgs) $(FlagParameterPrefix)nodereuse $(ArcadeFalseBoolBuildArg) + $(InnerBuildArgs) $(FlagParameterPrefix)warnAsError $(ArcadeFalseBoolBuildArg) + $(InnerBuildArgs) $(FlagParameterPrefix)usemonoruntime + + $(InnerBuildArgs) --outputrid $(TargetRid) $(InnerBuildArgs) /p:PackageOS=$(RuntimeOS) /p:ToolsOS=$(RuntimeOS) $(InnerBuildArgs) /p:AdditionalRuntimeIdentifierParent=$(BaseOS) + - $(InnerBuildArgs) /p:ArcadeBuildFromSource=true + $(InnerBuildArgs) /p:ArcadeBuildFromSource=true + $(InnerBuildArgs) /p:ArcadeBuildVertical=true $(InnerBuildArgs) /p:OfficialBuildId=$(OfficialBuildId) $(InnerBuildArgs) /p:ContinuousIntegrationBuild=$(ContinuousIntegrationBuild) - $(InnerBuildArgs) --usemonoruntime $(InnerBuildArgs) /p:PortableBuild=$(PortableBuild) - $(InnerBuildArgs) /p:DotnetBuildVertical=$(DotnetBuildVertical) @@ -66,7 +66,7 @@ Runtime artifacts are too large to fit into a single package (Azure DevOps feeds 500 mb constraint). Split large components into separate packages. --> - + @@ -75,7 +75,7 @@ Category="Crossgen2Pack" /> From 4168b2b364cc19972a51fcd86e4a5ebf44991143 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 22 Jan 2024 09:48:03 -0800 Subject: [PATCH 174/189] Switch RuntimeProperties host test to pre-built test assets (#97245) --- .../Assets/Projects/HelloWorld/Program.cs | 13 ++ .../TestProjects/RuntimeProperties/Program.cs | 28 ----- .../RuntimeProperties.csproj | 9 -- .../HostActivation.Tests/RuntimeProperties.cs | 119 +++++++----------- 4 files changed, 60 insertions(+), 109 deletions(-) delete mode 100644 src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs delete mode 100644 src/installer/tests/Assets/TestProjects/RuntimeProperties/RuntimeProperties.csproj diff --git a/src/installer/tests/Assets/Projects/HelloWorld/Program.cs b/src/installer/tests/Assets/Projects/HelloWorld/Program.cs index f16c63a00ba8b3..f526fd671d632d 100644 --- a/src/installer/tests/Assets/Projects/HelloWorld/Program.cs +++ b/src/installer/tests/Assets/Projects/HelloWorld/Program.cs @@ -26,6 +26,19 @@ public static void Main(string[] args) PropertyInfo property = asm.GetType("SharedLibrary.SharedType").GetProperty("Value"); Console.WriteLine($"SharedLibrary.SharedType.Value = {property.GetValue(null)}"); break; + case "print_properties": + foreach (string propertyName in args[1..]) + { + var propertyValue = (string)System.AppContext.GetData(propertyName); + if (string.IsNullOrEmpty(propertyValue)) + { + Console.WriteLine($"Property '{propertyName}' was not found."); + continue; + } + + Console.WriteLine($"AppContext.GetData({propertyName}) = {propertyValue}"); + } + break; case "throw_exception": throw new Exception("Goodbye World!"); default: diff --git a/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs b/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs deleted file mode 100644 index 96fa3b2adbad28..00000000000000 --- a/src/installer/tests/Assets/TestProjects/RuntimeProperties/Program.cs +++ /dev/null @@ -1,28 +0,0 @@ -// 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 RuntimeProperties -{ - public static class Program - { - public static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - Console.WriteLine(string.Join(Environment.NewLine, args)); - - foreach (string propertyName in args) - { - var propertyValue = (string)System.AppContext.GetData(propertyName); - if (string.IsNullOrEmpty(propertyValue)) - { - Console.WriteLine($"Property '{propertyName}' was not found."); - continue; - } - - Console.WriteLine($"AppContext.GetData({propertyName}) = {propertyValue}"); - } - } - } -} diff --git a/src/installer/tests/Assets/TestProjects/RuntimeProperties/RuntimeProperties.csproj b/src/installer/tests/Assets/TestProjects/RuntimeProperties/RuntimeProperties.csproj deleted file mode 100644 index f413febe3ff59a..00000000000000 --- a/src/installer/tests/Assets/TestProjects/RuntimeProperties/RuntimeProperties.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - $(NetCoreAppCurrent) - Exe - $(MNAVersion) - - - diff --git a/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs b/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs index be53e8195e5f54..f734a1f2af9e44 100644 --- a/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs +++ b/src/installer/tests/HostActivation.Tests/RuntimeProperties.cs @@ -4,13 +4,16 @@ using System; using System.IO; using Microsoft.DotNet.Cli.Build; +using Microsoft.DotNet.CoreSetup.Test; using Xunit; -namespace Microsoft.DotNet.CoreSetup.Test.HostActivation +namespace HostActivation.Tests { public class RuntimeProperties : IClassFixture { - private SharedTestState sharedState; + private const string PrintProperties = "print_properties"; + + private readonly SharedTestState sharedState; public RuntimeProperties(RuntimeProperties.SharedTestState fixture) { @@ -20,53 +23,39 @@ public RuntimeProperties(RuntimeProperties.SharedTestState fixture) [Fact] public void AppConfigProperty_AppCanGetData() { - var fixture = sharedState.RuntimePropertiesFixture - .Copy(); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - dotnet.Exec(appDll, sharedState.AppTestPropertyName) + sharedState.DotNet.Exec(sharedState.App.AppDll, PrintProperties, SharedTestState.AppTestPropertyName) .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdErrContaining($"Property {sharedState.AppTestPropertyName} = {sharedState.AppTestPropertyValue}") - .And.HaveStdOutContaining($"AppContext.GetData({sharedState.AppTestPropertyName}) = {sharedState.AppTestPropertyValue}"); + .And.HaveStdErrContaining($"Property {SharedTestState.AppTestPropertyName} = {SharedTestState.AppTestPropertyValue}") + .And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.AppTestPropertyName}) = {SharedTestState.AppTestPropertyValue}"); } [Fact] public void FrameworkConfigProperty_AppCanGetData() { - var fixture = sharedState.RuntimePropertiesFixture - .Copy(); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - dotnet.Exec(appDll, sharedState.FrameworkTestPropertyName) + sharedState.DotNet.Exec(sharedState.App.AppDll, PrintProperties, SharedTestState.FrameworkTestPropertyName) .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdErrContaining($"Property {sharedState.FrameworkTestPropertyName} = {sharedState.FrameworkTestPropertyValue}") - .And.HaveStdOutContaining($"AppContext.GetData({sharedState.FrameworkTestPropertyName}) = {sharedState.FrameworkTestPropertyValue}"); + .And.HaveStdErrContaining($"Property {SharedTestState.FrameworkTestPropertyName} = {SharedTestState.FrameworkTestPropertyValue}") + .And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.FrameworkTestPropertyName}) = {SharedTestState.FrameworkTestPropertyValue}"); } [Fact] public void DuplicateConfigProperty_AppConfigValueUsed() { - var fixture = sharedState.RuntimePropertiesFixture - .Copy(); - - RuntimeConfig.FromFile(fixture.TestProject.RuntimeConfigJson) - .WithProperty(sharedState.FrameworkTestPropertyName, sharedState.AppTestPropertyValue) + var app = sharedState.App.Copy(); + RuntimeConfig.FromFile(app.RuntimeConfigJson) + .WithProperty(SharedTestState.FrameworkTestPropertyName, SharedTestState.AppTestPropertyValue) .Save(); - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - dotnet.Exec(appDll, sharedState.FrameworkTestPropertyName) + sharedState.DotNet.Exec(app.AppDll, PrintProperties, SharedTestState.FrameworkTestPropertyName) .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdErrContaining($"Property {sharedState.FrameworkTestPropertyName} = {sharedState.AppTestPropertyValue}") - .And.HaveStdOutContaining($"AppContext.GetData({sharedState.FrameworkTestPropertyName}) = {sharedState.AppTestPropertyValue}"); + .And.HaveStdErrContaining($"Property {SharedTestState.FrameworkTestPropertyName} = {SharedTestState.AppTestPropertyValue}") + .And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.FrameworkTestPropertyName}) = {SharedTestState.AppTestPropertyValue}"); } [Fact] @@ -77,38 +66,30 @@ public void HostFxrPathProperty_SetWhenRunningSDKCommand() .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdErrContaining($"Property {sharedState.HostFxrPathPropertyName} = {dotnet.GreatestVersionHostFxrFilePath}"); + .And.HaveStdErrContaining($"Property {SharedTestState.HostFxrPathPropertyName} = {dotnet.GreatestVersionHostFxrFilePath}"); } [Fact] public void HostFxrPathProperty_NotVisibleFromApp() { - var fixture = sharedState.RuntimePropertiesFixture - .Copy(); - - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - dotnet.Exec(appDll, sharedState.HostFxrPathPropertyName) + sharedState.DotNet.Exec(sharedState.App.AppDll, PrintProperties, SharedTestState.HostFxrPathPropertyName) .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdOutContaining($"Property '{sharedState.HostFxrPathPropertyName}' was not found."); + .And.HaveStdOutContaining($"Property '{SharedTestState.HostFxrPathPropertyName}' was not found."); } [Fact] public void DuplicateCommonProperty_Fails() { - var fixture = sharedState.RuntimePropertiesFixture - .Copy(); + var app = sharedState.App.Copy(); string name = "RUNTIME_IDENTIFIER"; - RuntimeConfig.FromFile(fixture.TestProject.RuntimeConfigJson) - .WithProperty(name, sharedState.AppTestPropertyValue) + RuntimeConfig.FromFile(app.RuntimeConfigJson) + .WithProperty(name, SharedTestState.AppTestPropertyValue) .Save(); - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - dotnet.Exec(appDll) + sharedState.DotNet.Exec(app.AppDll) .EnableTracingAndCaptureOutputs() .Execute() .Should().Fail() @@ -118,43 +99,41 @@ public void DuplicateCommonProperty_Fails() [Fact] public void SpecifiedInConfigAndDevConfig_ConfigWins() { - var fixture = sharedState.RuntimePropertiesFixture - .Copy(); + var app = sharedState.App.Copy(); - RuntimeConfig.FromFile(fixture.TestProject.RuntimeDevConfigJson) - .WithProperty(sharedState.AppTestPropertyName, "VALUE_FROM_DEV_CONFIG") + RuntimeConfig.FromFile(app.RuntimeDevConfigJson) + .WithProperty(SharedTestState.AppTestPropertyName, "VALUE_FROM_DEV_CONFIG") .Save(); - var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll; - dotnet.Exec(appDll, sharedState.AppTestPropertyName) + sharedState.DotNet.Exec(app.AppDll, PrintProperties, SharedTestState.AppTestPropertyName) .EnableTracingAndCaptureOutputs() .Execute() .Should().Pass() - .And.HaveStdErrContaining($"Property {sharedState.AppTestPropertyName} = {sharedState.AppTestPropertyValue}") - .And.HaveStdOutContaining($"AppContext.GetData({sharedState.AppTestPropertyName}) = {sharedState.AppTestPropertyValue}"); + .And.HaveStdErrContaining($"Property {SharedTestState.AppTestPropertyName} = {SharedTestState.AppTestPropertyValue}") + .And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.AppTestPropertyName}) = {SharedTestState.AppTestPropertyValue}"); } public class SharedTestState : IDisposable { - public TestProjectFixture RuntimePropertiesFixture { get; } - public RepoDirectoriesProvider RepoDirectories { get; } - public DotNetCli MockSDK { get; } + public const string AppTestPropertyName = "APP_TEST_PROPERTY"; + public const string AppTestPropertyValue = "VALUE_FROM_APP"; + public const string FrameworkTestPropertyName = "FRAMEWORK_TEST_PROPERTY"; + public const string FrameworkTestPropertyValue = "VALUE_FROM_FRAMEWORK"; + public const string HostFxrPathPropertyName = "HOSTFXR_PATH"; - public string AppTestPropertyName => "APP_TEST_PROPERTY"; - public string AppTestPropertyValue => "VALUE_FROM_APP"; - public string FrameworkTestPropertyName => "FRAMEWORK_TEST_PROPERTY"; - public string FrameworkTestPropertyValue => "VALUE_FROM_FRAMEWORK"; - public string HostFxrPathPropertyName => "HOSTFXR_PATH"; + public TestApp App { get; } + public DotNetCli MockSDK { get; } + public DotNetCli DotNet { get; } private readonly TestArtifact copiedDotnet; public SharedTestState() { - copiedDotnet = new TestArtifact(Path.Combine(TestArtifact.TestArtifactsPath, "runtimeProperties")); - SharedFramework.CopyDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), copiedDotnet.Location); + // Make a copy of the built .NET, as we will update the framework's runtime config + copiedDotnet = TestArtifact.Create("runtimeProperties"); + SharedFramework.CopyDirectory(TestContext.BuiltDotNet.BinPath, copiedDotnet.Location); - MockSDK = new DotNetBuilder(copiedDotnet.Location, Path.Combine(TestArtifact.TestArtifactsPath, "sharedFrameworkPublish"), "exe") + MockSDK = new DotNetBuilder(copiedDotnet.Location, TestContext.BuiltDotNet.BinPath, "mocksdk") .AddMicrosoftNETCoreAppFrameworkMockCoreClr("9999.0.0") .AddMockSDK("9999.0.0-dev", "9999.0.0") .Build(); @@ -167,24 +146,20 @@ public SharedTestState() } }"); - RepoDirectories = new RepoDirectoriesProvider(builtDotnet: copiedDotnet.Location); - - RuntimePropertiesFixture = new TestProjectFixture("RuntimeProperties", RepoDirectories) - .EnsureRestored() - .BuildProject(); - - RuntimeConfig.FromFile(RuntimePropertiesFixture.TestProject.RuntimeConfigJson) + App = TestApp.CreateFromBuiltAssets("HelloWorld"); + RuntimeConfig.FromFile(App.RuntimeConfigJson) .WithProperty(AppTestPropertyName, AppTestPropertyValue) .Save(); - RuntimeConfig.FromFile(Path.Combine(RuntimePropertiesFixture.BuiltDotnet.GreatestVersionSharedFxPath, "Microsoft.NETCore.App.runtimeconfig.json")) + DotNet = new DotNetCli(copiedDotnet.Location); + RuntimeConfig.FromFile(Path.Combine(DotNet.GreatestVersionSharedFxPath, "Microsoft.NETCore.App.runtimeconfig.json")) .WithProperty(FrameworkTestPropertyName, FrameworkTestPropertyValue) .Save(); } public void Dispose() { - RuntimePropertiesFixture.Dispose(); + App?.Dispose(); copiedDotnet.Dispose(); } } From 41494bae8f78032280909f8c7c2ca10350fdfe9e Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 22 Jan 2024 10:03:42 -0800 Subject: [PATCH 175/189] Fix Type.GetHashCode for RuntimeTypes w/o MethodTable (#97195) The hashcode for these was always 0 before this change. --- .../System.Private.CoreLib/src/System/RuntimeType.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs index bb9cd160986db2..59423d258179c7 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs @@ -204,7 +204,12 @@ public override Array GetEnumValuesAsUnderlyingType() } public override int GetHashCode() - => ((nuint)_pUnderlyingEEType).GetHashCode(); + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return ((nuint)pEEType).GetHashCode(); + return RuntimeHelpers.GetHashCode(this); + } public override RuntimeTypeHandle TypeHandle { From e99836a899bdb97be5afbbed8d90573ee5635b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 22 Jan 2024 19:08:28 +0100 Subject: [PATCH 176/189] Disable msbuild terminal logger (#97309) * Disable msbuild terminal logger https://github.com/dotnet/runtime/issues/97211 * Update Directory.Build.rsp --- Directory.Build.rsp | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Directory.Build.rsp diff --git a/Directory.Build.rsp b/Directory.Build.rsp new file mode 100644 index 00000000000000..535f93da1e28f8 --- /dev/null +++ b/Directory.Build.rsp @@ -0,0 +1,2 @@ +# disable terminal logger for now: https://github.com/dotnet/runtime/issues/97211 +-tl:false From 4ac8ca33d0ae0bee16081f0dcea425f42eac4bb6 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 22 Jan 2024 13:49:46 -0500 Subject: [PATCH 177/189] Move regex test to use RemoteExecutor (#97317) --- .../Regex.CompileToAssembly.Tests.cs | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.CompileToAssembly.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.CompileToAssembly.Tests.cs index 9a95653af72e77..a669f65bd023ed 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.CompileToAssembly.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.CompileToAssembly.Tests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using Microsoft.DotNet.RemoteExecutor; using Xunit; namespace System.Text.RegularExpressions.Tests @@ -15,6 +16,7 @@ public class RegexCompileToAssemblyTests : FileCleanupTestBase { public static bool IsDebug => typeof(Regex).Assembly.GetCustomAttributes(false).OfType().Any(da => da.IsJITTrackingEnabled); public static bool IsRelease => !IsDebug; + public static bool IsDebugAndRemoteExecutorSupported => IsDebug && RemoteExecutor.IsSupported; [ConditionalFact(nameof(IsRelease))] public void CompileToAssembly_PNSE() @@ -39,33 +41,36 @@ [new CustomAttributeBuilder(typeof(AssemblyCompanyAttribute).GetConstructor([typ "resourceFile")); } - [ConditionalFact(nameof(IsDebug))] + [ConditionalFact(nameof(IsDebugAndRemoteExecutorSupported))] public void CompileToAssembly_SimpleUseInDebug() { - (RegexCompilationInfo rci, string validInput, string invalidInput)[] regexes = - [ - (new RegexCompilationInfo("abcd", RegexOptions.None, "Type1", "Namespace1", ispublic: true), "123abcd123", "123abed123"), - (new RegexCompilationInfo("(a|b|cde)+", RegexOptions.None, "Type2", "Namespace2.Sub", ispublic: true), "abcde", "cd"), - ]; + RemoteExecutor.Invoke(() => + { + (RegexCompilationInfo rci, string validInput, string invalidInput)[] regexes = + [ + (new RegexCompilationInfo("abcd", RegexOptions.None, "Type1", "Namespace1", ispublic: true), "123abcd123", "123abed123"), + (new RegexCompilationInfo("(a|b|cde)+", RegexOptions.None, "Type2", "Namespace2.Sub", ispublic: true), "abcde", "cd"), + ]; - string assemblyName = Path.GetRandomFileName(); + string assemblyName = Path.GetRandomFileName(); - string cwd = Environment.CurrentDirectory; - Environment.CurrentDirectory = TestDirectory; - try - { - Regex.CompileToAssembly(regexes.Select(r => r.rci).ToArray(), new AssemblyName(assemblyName)); - } - finally - { - Environment.CurrentDirectory = cwd; - } + string cwd = Environment.CurrentDirectory; + Environment.CurrentDirectory = TestDirectory; + try + { + Regex.CompileToAssembly(regexes.Select(r => r.rci).ToArray(), new AssemblyName(assemblyName)); + } + finally + { + Environment.CurrentDirectory = cwd; + } - string assemblyPath = Path.Combine(TestDirectory, assemblyName + ".dll"); - Assert.True(File.Exists(assemblyPath)); + string assemblyPath = Path.Combine(TestDirectory, assemblyName + ".dll"); + Assert.True(File.Exists(assemblyPath)); - // Uncomment to save the assembly to the desktop for inspection: - // File.Copy(assemblyPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Path.GetFileName(assemblyPath))); + // Uncomment to save the assembly to the desktop for inspection: + // File.Copy(assemblyPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Path.GetFileName(assemblyPath))); + }).Dispose(); } } } From ccdaf39a85c7a1d2ddd1b4051072078a1340250d Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Mon, 22 Jan 2024 11:10:05 -0800 Subject: [PATCH 178/189] NativeAot: Enable GC event tracing on standalone GC (#97257) * NativeAot: Enable GC event tracing on standalone GC * Feedback: make GC event stashing more pay for play * Fix build by adding dependency on generated files. --- src/coreclr/nativeaot/Runtime/Crst.h | 1 + .../nativeaot/Runtime/Full/CMakeLists.txt | 10 ++++ .../nativeaot/Runtime/clrgc.disabled.cpp | 17 +++++-- .../nativeaot/Runtime/clrgc.enabled.cpp | 49 +++++++++++++++++++ .../nativeaot/Runtime/gcheaputilities.cpp | 6 --- src/coreclr/nativeaot/Runtime/startup.cpp | 3 ++ 6 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/Crst.h b/src/coreclr/nativeaot/Runtime/Crst.h index b4112a174b2744..31bf8fde9eec8a 100644 --- a/src/coreclr/nativeaot/Runtime/Crst.h +++ b/src/coreclr/nativeaot/Runtime/Crst.h @@ -23,6 +23,7 @@ enum CrstType CrstYieldProcessorNormalized, CrstEventPipe, CrstEventPipeConfig, + CrstGcEvent, }; enum CrstFlags diff --git a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt index 84a4731559c24e..e665a6c88ee10b 100644 --- a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt @@ -33,7 +33,13 @@ add_library(Runtime.ServerGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOU add_dependencies(Runtime.ServerGC aot_eventing_headers) add_library(standalonegc-disabled STATIC ${STANDALONEGC_DISABLED_SOURCES}) +add_dependencies(standalonegc-disabled aot_eventing_headers) add_library(standalonegc-enabled STATIC ${STANDALONEGC_ENABLED_SOURCES}) +add_dependencies(standalonegc-enabled aot_eventing_headers) +if(CLR_CMAKE_TARGET_WIN32) + add_dependencies(standalonegc-disabled aot_etw_headers) + add_dependencies(standalonegc-enabled aot_etw_headers) +endif() if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64) add_library(Runtime.VxsortEnabled STATIC ${VXSORT_SOURCES}) @@ -45,8 +51,12 @@ target_compile_definitions(Runtime.ServerGC PRIVATE -DFEATURE_SVR_GC) if (CLR_CMAKE_TARGET_WIN32) add_library(standalonegc-disabled.GuardCF STATIC ${STANDALONEGC_DISABLED_SOURCES}) set_target_properties(standalonegc-disabled.GuardCF PROPERTIES CLR_CONTROL_FLOW_GUARD ON) + add_dependencies(standalonegc-disabled.GuardCF aot_eventing_headers) + add_dependencies(standalonegc-disabled.GuardCF aot_etw_headers) add_library(standalonegc-enabled.GuardCF STATIC ${STANDALONEGC_ENABLED_SOURCES}) set_target_properties(standalonegc-enabled.GuardCF PROPERTIES CLR_CONTROL_FLOW_GUARD ON) + add_dependencies(standalonegc-enabled.GuardCF aot_eventing_headers) + add_dependencies(standalonegc-enabled.GuardCF aot_etw_headers) add_library(Runtime.ServerGC.GuardCF STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${RUNTIME_SOURCES_ARCH_ASM} ${SERVER_GC_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) target_compile_definitions(Runtime.ServerGC.GuardCF PRIVATE -DFEATURE_SVR_GC) set_target_properties(Runtime.ServerGC.GuardCF PROPERTIES CLR_CONTROL_FLOW_GUARD ON) diff --git a/src/coreclr/nativeaot/Runtime/clrgc.disabled.cpp b/src/coreclr/nativeaot/Runtime/clrgc.disabled.cpp index 81e6bffa5254cc..e03c0a25351ec0 100644 --- a/src/coreclr/nativeaot/Runtime/clrgc.disabled.cpp +++ b/src/coreclr/nativeaot/Runtime/clrgc.disabled.cpp @@ -1,12 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include "CommonTypes.h" -#include "CommonMacros.h" +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "gceventstatus.h" + +void InitializeGCEventLock() +{ +} HRESULT InitializeDefaultGC(); HRESULT InitializeGCSelector() { return InitializeDefaultGC(); -} \ No newline at end of file +} + +void GCHeapUtilities::RecordEventStateChange(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level) +{ + GCEventStatus::Set(isPublicProvider ? GCEventProvider_Default : GCEventProvider_Private, keywords, level); +} diff --git a/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp b/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp index 136885cd0ad50d..d35127dc1b0e77 100644 --- a/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp +++ b/src/coreclr/nativeaot/Runtime/clrgc.enabled.cpp @@ -32,6 +32,20 @@ GC_LOAD_STATUS g_gc_load_status = GC_LOAD_STATUS_BEFORE_START; // The version of the GC that we have loaded. VersionInfo g_gc_version_info; +CrstStatic g_eventStashLock; + +GCEventLevel g_stashedLevel = GCEventLevel_None; +GCEventKeyword g_stashedKeyword = GCEventKeyword_None; +GCEventLevel g_stashedPrivateLevel = GCEventLevel_None; +GCEventKeyword g_stashedPrivateKeyword = GCEventKeyword_None; + +BOOL g_gcEventTracingInitialized = FALSE; + +void InitializeGCEventLock() +{ + g_eventStashLock.InitNoThrow(CrstGcEvent); +} + HRESULT InitializeStandaloneGC(); HRESULT InitializeGCSelector() @@ -137,6 +151,12 @@ HRESULT GCHeapUtilities::InitializeStandaloneGC() if (initResult == S_OK) { g_pGCHeap = heap; + { + CrstHolder lh(&g_eventStashLock); + g_pGCHeap->ControlEvents(g_stashedKeyword, g_stashedLevel); + g_pGCHeap->ControlPrivateEvents(g_stashedPrivateKeyword, g_stashedPrivateLevel); + g_gcEventTracingInitialized = TRUE; + } g_pGCHandleManager = manager; g_gcDacGlobals = &g_gc_dac_vars; LOG((LF_GC, LL_INFO100, "GC load successful\n")); @@ -148,3 +168,32 @@ HRESULT GCHeapUtilities::InitializeStandaloneGC() return initResult; } + +void GCHeapUtilities::RecordEventStateChange(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level) +{ + CrstHolder lh(&g_eventStashLock); + if (g_gcEventTracingInitialized) + { + if (isPublicProvider) + { + g_pGCHeap->ControlEvents(keywords, level); + } + else + { + g_pGCHeap->ControlPrivateEvents(keywords, level); + } + } + else + { + if (isPublicProvider) + { + g_stashedKeyword = keywords; + g_stashedLevel = level; + } + else + { + g_stashedPrivateKeyword = keywords; + g_stashedPrivateLevel = level; + } + } +} diff --git a/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp b/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp index b7cebfb6db9241..42f7928cd9e252 100644 --- a/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp +++ b/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp @@ -87,10 +87,4 @@ HRESULT GCHeapUtilities::InitializeDefaultGC() return initResult; } -void GCHeapUtilities::RecordEventStateChange(bool isPublicProvider, GCEventKeyword keywords, GCEventLevel level) -{ - // NativeAOT does not support standalone GC. Call GCEventStatus directly to keep things simple. - GCEventStatus::Set(isPublicProvider ? GCEventProvider_Default : GCEventProvider_Private, keywords, level); -} - #endif // DACCESS_COMPILE diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index a78c6a8c2ce807..f1da550fc01cc9 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -88,6 +88,7 @@ extern "C" volatile GSCookie __security_cookie = 0; static RhConfig g_sRhConfig; RhConfig* g_pRhConfig = &g_sRhConfig; +void InitializeGCEventLock(); bool InitializeGC(); static bool InitDLL(HANDLE hPalInstance) @@ -100,6 +101,8 @@ static bool InitDLL(HANDLE hPalInstance) return false; #endif + InitializeGCEventLock(); + #ifdef FEATURE_PERFTRACING // Initialize EventPipe EventPipe_Initialize(); From 971ebdbd0fe335c34d440269af8055c6eea87286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 22 Jan 2024 20:57:40 +0100 Subject: [PATCH 179/189] Don't assume first argument in build.ps1 is subset (#97330) * Don't assume first argument in build.ps1 is subset We assumed the first argument to the build script is the subset, but that doesn't work if you have `build.cmd /p:SomeProperty=value` like we have in the VMR. Instead do what build.sh does and check whether the first argument looks like a valid subset string and only use it in that case. Fixes https://github.com/dotnet/source-build/issues/3790 * Update eng/build.ps1 Co-authored-by: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> --------- Co-authored-by: Viktor Hofer Co-authored-by: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> --- eng/build.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/eng/build.ps1 b/eng/build.ps1 index 687d5bbc726843..84670c6a3d4804 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -12,7 +12,7 @@ Param( [string]$testscope, [switch]$testnobuild, [ValidateSet("x86","x64","arm","arm64","wasm")][string[]][Alias('a')]$arch = @([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant()), - [Parameter(Position=0)][string][Alias('s')]$subset, + [string][Alias('s')]$subset, [ValidateSet("Debug","Release","Checked")][string][Alias('rc')]$runtimeConfiguration, [ValidateSet("Debug","Release")][string][Alias('lc')]$librariesConfiguration, [ValidateSet("CoreCLR","Mono")][string][Alias('rf')]$runtimeFlavor, @@ -130,6 +130,13 @@ if ($help) { exit 0 } +# check the first argument if subset is not explicitly passed in +if (-not $PSBoundParameters.ContainsKey("subset") -and $properties.Length -gt 0 -and $properties[0] -match '^[a-zA-Z\.\+]+$') { + $subset = $properties[0] + $PSBoundParameters.Add("subset", $subset) + $properties = $properties | Select-Object -Skip 1 +} + if ($subset -eq 'help') { Invoke-Expression "& `"$PSScriptRoot/common/build.ps1`" -restore -build /p:subset=help /clp:nosummary" exit 0 From 7e55140361c309e336009ebf7bf6773db1bfa119 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 22 Jan 2024 21:54:47 +0100 Subject: [PATCH 180/189] [browser] fix server initiated full close (#97320) --- .../System.Net.WebSockets.Client/tests/CloseTest.cs | 4 ---- src/mono/browser/runtime/web-socket.ts | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs index 81f80a1e647bdb..c768fab5972a8a 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/CloseTest.cs @@ -266,7 +266,6 @@ public async Task CloseOutputAsync_ClientInitiated_CanReceive_CanClose(Uri serve [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] public async Task CloseOutputAsync_ServerInitiated_CanReceive(Uri server) { - string message = "Hello WebSockets!"; var expectedCloseStatus = WebSocketCloseStatus.NormalClosure; var expectedCloseDescription = ".shutdownafter"; @@ -303,9 +302,6 @@ await cws.SendAsync( Assert.Equal(WebSocketState.CloseReceived, cws.State); - // Should be able to send. - await cws.SendAsync(WebSocketData.GetBufferFromText(message), WebSocketMessageType.Text, true, cts.Token); - // Cannot change the close status/description with the final close. var closeStatus = PlatformDetection.IsNotBrowser ? WebSocketCloseStatus.InvalidPayloadData : (WebSocketCloseStatus)3210; var closeDescription = "CloseOutputAsync_Client_Description"; diff --git a/src/mono/browser/runtime/web-socket.ts b/src/mono/browser/runtime/web-socket.ts index bbcac93a1bd790..8f95dee771311f 100644 --- a/src/mono/browser/runtime/web-socket.ts +++ b/src/mono/browser/runtime/web-socket.ts @@ -131,6 +131,11 @@ export function ws_wasm_send(ws: WebSocketExtension, buffer_ptr: VoidPtr, buffer if (ws[wasm_ws_is_aborted] || ws[wasm_ws_close_sent]) { return rejectedPromise("InvalidState: The WebSocket is not connected."); } + if (ws.readyState == WebSocket.CLOSED) { + // this is server initiated close but not partial close + // because CloseOutputAsync_ServerInitiated_CanSend expectations, we don't fail here + return resolvedPromise(); + } const buffer_view = new Uint8Array(localHeapViewU8().buffer, buffer_ptr, buffer_length); const whole_buffer = _mono_wasm_web_socket_send_buffering(ws, buffer_view, message_type, end_of_message); From b32a592bbd22f051cbab567b51c123af22ea918d Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 22 Jan 2024 15:03:23 -0600 Subject: [PATCH 181/189] Remove workaround (#97335) --- .../WorkloadManifest.targets.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in index 028b54fd5677f4..86b8c527efa08c 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in @@ -8,7 +8,7 @@ true - true + false true From 2dc8c956f98389418400dda5bda663d67cbea938 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 22 Jan 2024 13:09:43 -0800 Subject: [PATCH 182/189] Clean up and consolidate HostModel tests. (#97244) - Merge HostModel AppHost/Bundle/ComHost tests into one `Microsoft.NET.HostModel.Tests` project - Move symbolic link tests into `HostActivation.Tests` - they are not testing HostModel - Rename `AppHostUpdateTests` to `CreateAppHost` - that is the API they are testing - Switch `AppWithUnknownLanguageResource` to pre-built asset - Move to `CreateAppHost` test class --- eng/Subsets.props | 6 +- src/installer/Microsoft.DotNet.CoreSetup.sln | 104 ++++++------------ .../AppHost.Bundle.Tests.csproj | 4 +- .../AppHost.Bundle.Tests/AppLaunch.cs | 0 .../BundleExtractToSpecificPath.cs | 0 .../BundleLocalizedApp.cs | 0 .../AppHost.Bundle.Tests/BundleProbe.cs | 0 .../AppHost.Bundle.Tests/BundleRename.cs | 0 .../BundledAppWithSubDirs.cs | 0 .../AppHost.Bundle.Tests/HammerServiceTest.cs | 2 +- .../SingleFileApiTests.cs | 0 .../AppHost.Bundle.Tests/StaticHost.cs | 0 .../AppWithUnknownLanguageResource.csproj | 2 - .../AppWithUnknownLanguageResource/Program.cs | 0 .../AppWithUnknownLanguageResource/test.rc | 0 .../AppWithUnknownLanguageResource/test.res | Bin .../SymbolicLinks.cs} | 14 +-- .../CreateAppHost.cs} | 61 +++++----- .../BundlerConsistencyTests.cs | 4 +- .../ClsidMapTests.cs | 4 +- .../RegFreeComManifestTests.cs | 27 +++-- .../AppWithUnknownLanguageResource.cs | 47 -------- ...crosoft.NET.HostModel.AppHost.Tests.csproj | 19 ---- ...icrosoft.NET.HostModel.Bundle.Tests.csproj | 19 ---- ...crosoft.NET.HostModel.ComHost.Tests.csproj | 19 ---- .../TestDirectory.cs | 36 ------ .../Microsoft.NET.HostModel.Tests.csproj | 18 +++ .../ResourceUpdaterTests.cs | 0 28 files changed, 107 insertions(+), 279 deletions(-) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj (77%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/AppLaunch.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/BundleLocalizedApp.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/BundleProbe.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/BundleRename.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/HammerServiceTest.cs (98%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/SingleFileApiTests.cs (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests => }/AppHost.Bundle.Tests/StaticHost.cs (100%) rename src/installer/tests/Assets/{TestProjects => Projects}/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj (72%) rename src/installer/tests/Assets/{TestProjects => Projects}/AppWithUnknownLanguageResource/Program.cs (100%) rename src/installer/tests/Assets/{TestProjects => Projects}/AppWithUnknownLanguageResource/test.rc (100%) rename src/installer/tests/Assets/{TestProjects => Projects}/AppWithUnknownLanguageResource/test.res (100%) rename src/installer/tests/{Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUsedWithSymbolicLinks.cs => HostActivation.Tests/SymbolicLinks.cs} (97%) rename src/installer/tests/Microsoft.NET.HostModel.Tests/{Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs => AppHost/CreateAppHost.cs} (92%) rename src/installer/tests/Microsoft.NET.HostModel.Tests/{Microsoft.NET.HostModel.Bundle.Tests => Bundle}/BundlerConsistencyTests.cs (99%) rename src/installer/tests/Microsoft.NET.HostModel.Tests/{Microsoft.NET.HostModel.ComHost.Tests => ComHost}/ClsidMapTests.cs (97%) rename src/installer/tests/Microsoft.NET.HostModel.Tests/{Microsoft.NET.HostModel.ComHost.Tests => ComHost}/RegFreeComManifestTests.cs (84%) delete mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppWithUnknownLanguageResource.cs delete mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/Microsoft.NET.HostModel.AppHost.Tests.csproj delete mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/Microsoft.NET.HostModel.Bundle.Tests.csproj delete mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/Microsoft.NET.HostModel.ComHost.Tests.csproj delete mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/TestDirectory.cs create mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Tests.csproj rename src/installer/tests/Microsoft.NET.HostModel.Tests/{Microsoft.NET.HostModel.AppHost.Tests => }/ResourceUpdaterTests.cs (100%) diff --git a/eng/Subsets.props b/eng/Subsets.props index b769e1c18a0d60..2ed013d619c209 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -495,11 +495,9 @@ - - - - + + diff --git a/src/installer/Microsoft.DotNet.CoreSetup.sln b/src/installer/Microsoft.DotNet.CoreSetup.sln index 7eb575cd0b7b38..e75c1e933954ea 100644 --- a/src/installer/Microsoft.DotNet.CoreSetup.sln +++ b/src/installer/Microsoft.DotNet.CoreSetup.sln @@ -15,15 +15,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtils", "tests\TestUtil EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel", "managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj", "{325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppHost.Bundle.Tests", "tests\Microsoft.NET.HostModel.Tests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj", "{2745A51D-3425-4F68-8349-A8B8BC27DD87}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.Bundle.Tests", "tests\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj", "{1E76A78E-9E39-480D-8CD3-B7D0A858FECB}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.CoreSetup.Packaging.Tests", "tests\Microsoft.DotNet.CoreSetup.Packaging.Tests\Microsoft.DotNet.CoreSetup.Packaging.Tests.csproj", "{F1584324-A41A-4CE7-B23F-DB2252CFE276}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.AppHost.Tests", "tests\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj", "{AA3430BA-AE4A-4D52-B29D-B072A646C230}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppHost.Bundle.Tests", "tests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj", "{0496C031-637B-4862-B7ED-B4505E68F241}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.ComHost.Tests", "tests\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.ComHost.Tests\Microsoft.NET.HostModel.ComHost.Tests.csproj", "{35EF7826-6D47-4C91-AFDF-91091C05FB13}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.Tests", "tests\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.Tests.csproj", "{9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -85,38 +81,6 @@ Global {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Debug|x64.ActiveCfg = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Debug|x64.Build.0 = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Release|Any CPU.Build.0 = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Release|x64.ActiveCfg = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.Release|x64.Build.0 = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {2745A51D-3425-4F68-8349-A8B8BC27DD87}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Debug|x64.ActiveCfg = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Debug|x64.Build.0 = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Release|Any CPU.Build.0 = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Release|x64.ActiveCfg = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.Release|x64.Build.0 = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {1E76A78E-9E39-480D-8CD3-B7D0A858FECB}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {F1584324-A41A-4CE7-B23F-DB2252CFE276}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1584324-A41A-4CE7-B23F-DB2252CFE276}.Debug|Any CPU.Build.0 = Debug|Any CPU {F1584324-A41A-4CE7-B23F-DB2252CFE276}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -133,38 +97,38 @@ Global {F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|x64.ActiveCfg = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|x64.Build.0 = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|Any CPU.Build.0 = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|x64.ActiveCfg = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|x64.Build.0 = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|x64.ActiveCfg = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Debug|x64.Build.0 = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|Any CPU.Build.0 = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|x64.ActiveCfg = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.Release|x64.Build.0 = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {35EF7826-6D47-4C91-AFDF-91091C05FB13}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Debug|x64.ActiveCfg = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Debug|x64.Build.0 = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Release|Any CPU.Build.0 = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Release|x64.ActiveCfg = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.Release|x64.Build.0 = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {0496C031-637B-4862-B7ED-B4505E68F241}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Debug|x64.ActiveCfg = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Debug|x64.Build.0 = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Release|Any CPU.Build.0 = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Release|x64.ActiveCfg = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.Release|x64.Build.0 = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {9212E3B2-DFCB-4A24-99AA-ED5F9ACAA87F}.RelWithDebInfo|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj b/src/installer/tests/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj similarity index 77% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj rename to src/installer/tests/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj index af4c08cf0869e5..e70ca66a4f0be5 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj +++ b/src/installer/tests/AppHost.Bundle.Tests/AppHost.Bundle.Tests.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/AppLaunch.cs b/src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/AppLaunch.cs rename to src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs b/src/installer/tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs rename to src/installer/tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs b/src/installer/tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs rename to src/installer/tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs b/src/installer/tests/AppHost.Bundle.Tests/BundleProbe.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs rename to src/installer/tests/AppHost.Bundle.Tests/BundleProbe.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleRename.cs b/src/installer/tests/AppHost.Bundle.Tests/BundleRename.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleRename.cs rename to src/installer/tests/AppHost.Bundle.Tests/BundleRename.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs rename to src/installer/tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs b/src/installer/tests/AppHost.Bundle.Tests/HammerServiceTest.cs similarity index 98% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs rename to src/installer/tests/AppHost.Bundle.Tests/HammerServiceTest.cs index 1b521e186a258f..0c7911a18f38d0 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs +++ b/src/installer/tests/AppHost.Bundle.Tests/HammerServiceTest.cs @@ -27,7 +27,7 @@ private void SingleFile_Apps_Are_Serviced() { var singleFile = sharedTestState.App.Bundle(); - // Create the servicing directory, and copy the servived DLL from service fixture to the servicing directory. + // Create the servicing directory, and copy the serviced DLL from service fixture to the servicing directory. var serviced = sharedTestState.ServicedLibrary; var serviceBasePath = Path.Combine(sharedTestState.App.Location, "coreservicing"); var servicePath = Path.Combine(serviceBasePath, "pkgs", serviced.Name, "1.0.0"); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/AppHost.Bundle.Tests/SingleFileApiTests.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs rename to src/installer/tests/AppHost.Bundle.Tests/SingleFileApiTests.cs diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs b/src/installer/tests/AppHost.Bundle.Tests/StaticHost.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs rename to src/installer/tests/AppHost.Bundle.Tests/StaticHost.cs diff --git a/src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj b/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj similarity index 72% rename from src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj rename to src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj index a1927cdcc709fa..4b68943005e42d 100644 --- a/src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj +++ b/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/AppWithUnknownLanguageResource.csproj @@ -3,8 +3,6 @@ $(NetCoreAppCurrent) Exe - $(TestTargetRid) - $(MNAVersion) test.res false diff --git a/src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/Program.cs b/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/Program.cs similarity index 100% rename from src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/Program.cs rename to src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/Program.cs diff --git a/src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/test.rc b/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/test.rc similarity index 100% rename from src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/test.rc rename to src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/test.rc diff --git a/src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/test.res b/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/test.res similarity index 100% rename from src/installer/tests/Assets/TestProjects/AppWithUnknownLanguageResource/test.res rename to src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource/test.res diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUsedWithSymbolicLinks.cs b/src/installer/tests/HostActivation.Tests/SymbolicLinks.cs similarity index 97% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUsedWithSymbolicLinks.cs rename to src/installer/tests/HostActivation.Tests/SymbolicLinks.cs index a8b6b3c89cda1e..45ad4f9961af07 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUsedWithSymbolicLinks.cs +++ b/src/installer/tests/HostActivation.Tests/SymbolicLinks.cs @@ -1,22 +1,22 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using FluentAssertions; -using Microsoft.DotNet.Cli.Build.Framework; -using Microsoft.DotNet.CoreSetup.Test; using System; using System.IO; using System.Linq; -using System.Runtime.InteropServices; + +using FluentAssertions; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; using Xunit; -namespace Microsoft.NET.HostModel.Tests +namespace HostActivation.Tests { - public class AppHostUsedWithSymbolicLinks : IClassFixture + public class SymbolicLinks : IClassFixture { private SharedTestState sharedTestState; - public AppHostUsedWithSymbolicLinks(AppHostUsedWithSymbolicLinks.SharedTestState fixture) + public SymbolicLinks(SymbolicLinks.SharedTestState fixture) { sharedTestState = fixture; } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs similarity index 92% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs rename to src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs index b4d038b99b5ce3..b125a8553cd418 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs @@ -2,20 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Text; + using FluentAssertions; -using Xunit; -using Microsoft.NET.HostModel.AppHost; using Microsoft.DotNet.CoreSetup.Test; -using System.Diagnostics; -using System.Reflection.PortableExecutable; +using Xunit; -namespace Microsoft.NET.HostModel.Tests +namespace Microsoft.NET.HostModel.AppHost.Tests { - public class AppHostUpdateTests + public class CreateAppHost { /// /// hash value embedded in default apphost executable in a place where the path to the app binary should be stored. @@ -24,7 +24,7 @@ public class AppHostUpdateTests private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder); [Fact] - public void ItEmbedsAppBinaryPath() + public void EmbedAppBinaryPath() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -48,12 +48,12 @@ public void ItEmbedsAppBinaryPath() BitConverter .ToUInt16(result, SubsystemOffset) .Should() - .Be(3); + .Be((ushort)Subsystem.WindowsCui); } } [Fact] - public void ItFailsToEmbedAppBinaryIfHashIsWrong() + public void PlaceholderHashNotFound_Fails() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -76,7 +76,7 @@ public void ItFailsToEmbedAppBinaryIfHashIsWrong() } [Fact] - public void ItFailsToEmbedTooLongAppBinaryPath() + public void AppBinaryPathTooLong_Fails() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -95,7 +95,7 @@ public void ItFailsToEmbedTooLongAppBinaryPath() } [Fact] - public void ItCanSetWindowsGUISubsystem() + public void GUISubsystem_WindowsPEFile() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -119,7 +119,7 @@ public void ItCanSetWindowsGUISubsystem() } [Fact] - public void ItFailsToSetGUISubsystemOnNonWindowsPEFile() + public void GUISubsystem_NonWindowsPEFile_Fails() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -144,7 +144,7 @@ public void ItFailsToSetGUISubsystemOnNonWindowsPEFile() } [Fact] - public void ItFailsToSetGUISubsystemWithWrongDefault() + public void GUISubsystem_WrongDefault_Fails() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -170,7 +170,7 @@ public void ItFailsToSetGUISubsystemWithWrongDefault() [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] - public void ItGeneratesExecutableImage() + public void ExecutableImage() { using TestDirectory testDirectory = TestDirectory.Create(); string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); @@ -198,30 +198,11 @@ public void ItGeneratesExecutableImage() .Be(expectedPermissions); } - [Fact] - public void CanCreateAppHost() - { - using (TestDirectory testDirectory = TestDirectory.Create()) - { - string sourceAppHostMock = PrepareAppHostMockFile(testDirectory); - File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly); - string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); - string appBinaryFilePath = "Test/App/Binary/Path.dll"; - HostWriter.CreateAppHost( - sourceAppHostMock, - destinationFilePath, - appBinaryFilePath, - windowsGraphicalUserInterface: false); - - File.SetAttributes(sourceAppHostMock, FileAttributes.Normal); - } - } - [Theory] [PlatformSpecific(TestPlatforms.OSX)] [InlineData("")] [InlineData("dir with spaces")] - public void CanCodeSignAppHostOnMacOS(string subdir) + public void CodeSignAppHostOnMacOS(string subdir) { using (TestDirectory testDirectory = TestDirectory.Create(subdir)) { @@ -258,7 +239,7 @@ public void CanCodeSignAppHostOnMacOS(string subdir) [Fact] [PlatformSpecific(TestPlatforms.OSX)] - public void ItDoesNotCodeSignAppHostByDefault() + public void DoesNotCodeSignAppHostByDefault() { using (TestDirectory testDirectory = TestDirectory.Create()) { @@ -321,6 +302,16 @@ public void CodeSigningFailuresThrow() } } + [Fact] + private void ResourceWithUnknownLanguage() + { + // https://github.com/dotnet/runtime/issues/88465 + using (TestApp app = TestApp.CreateFromBuiltAssets("AppWithUnknownLanguageResource")) + { + app.CreateAppHost(); + } + } + private string PrepareAppHostMockFile(TestDirectory testDirectory, Action customize = null) { // For now we're testing the AppHost on Windows PE files only. diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Bundle/BundlerConsistencyTests.cs similarity index 99% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs rename to src/installer/tests/Microsoft.NET.HostModel.Tests/Bundle/BundlerConsistencyTests.cs index def7820bc74a46..a48b5ae13e493f 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Bundle/BundlerConsistencyTests.cs @@ -6,13 +6,13 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; + using FluentAssertions; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; -using Microsoft.NET.HostModel.Bundle; using Xunit; -namespace Microsoft.NET.HostModel.Tests +namespace Microsoft.NET.HostModel.Bundle.Tests { public class BundlerConsistencyTests : IClassFixture { diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/ComHost/ClsidMapTests.cs similarity index 97% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs rename to src/installer/tests/Microsoft.NET.HostModel.Tests/ComHost/ClsidMapTests.cs index 4cfa179ee605c5..af0c0308b7c659 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/ClsidMapTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/ComHost/ClsidMapTests.cs @@ -107,8 +107,8 @@ public void ExplicitlyEmptyProgIdNotInClsidMap() private JObject CreateClsidMap(TestApp library) { - using var testDirectory = TestDirectory.Create(); - string clsidMapPath = Path.Combine(testDirectory.Path, "test.clsidmap"); + using var testDirectory = TestArtifact.Create("clsidmap"); + string clsidMapPath = Path.Combine(testDirectory.Location, "test.clsidmap"); using (var assemblyStream = new FileStream(library.AppDll, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read)) using (PEReader peReader = new PEReader(assemblyStream)) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/RegFreeComManifestTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/ComHost/RegFreeComManifestTests.cs similarity index 84% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/RegFreeComManifestTests.cs rename to src/installer/tests/Microsoft.NET.HostModel.Tests/ComHost/RegFreeComManifestTests.cs index 9a4f47e79b72d9..042553509a4607 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/RegFreeComManifestTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/ComHost/RegFreeComManifestTests.cs @@ -1,14 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Xml.Linq; + +using Microsoft.DotNet.CoreSetup.Test; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.NET.HostModel.ComHost.Tests @@ -20,19 +19,19 @@ public class RegFreeComManifestTests [Fact] public void RegFreeComManifestCorrectlyIncludesComHostFile() { - using TestDirectory directory = TestDirectory.Create(); + using TestArtifact directory = TestArtifact.Create(nameof(RegFreeComManifestCorrectlyIncludesComHostFile)); JObject clsidMap = new JObject { }; - string clsidmapPath = Path.Combine(directory.Path, "test.clsidmap"); + string clsidmapPath = Path.Combine(directory.Location, "test.clsidmap"); string json = JsonConvert.SerializeObject(clsidMap); string comHostName = "comhost.dll"; File.WriteAllText(clsidmapPath, json); - string regFreeComManifestPath = Path.Combine(directory.Path, "test.manifest"); + string regFreeComManifestPath = Path.Combine(directory.Location, "test.manifest"); RegFreeComManifest.CreateManifestFromClsidmap("assemblyName", comHostName, "1.0.0.0", clsidmapPath, regFreeComManifestPath); @@ -49,7 +48,7 @@ public void RegFreeComManifestCorrectlyIncludesComHostFile() [Fact] public void EntryInClsidMapAddedToRegFreeComManifest() { - using TestDirectory directory = TestDirectory.Create(); + using TestArtifact directory = TestArtifact.Create(nameof(EntryInClsidMapAddedToRegFreeComManifest)); string guid = "{190f1974-fa98-4922-8ed4-cf748630abbe}"; string assemblyName = "ComLibrary"; string typeName = "ComLibrary.Server"; @@ -62,13 +61,13 @@ public void EntryInClsidMapAddedToRegFreeComManifest() } }; - string clsidmapPath = Path.Combine(directory.Path, "test.clsidmap"); + string clsidmapPath = Path.Combine(directory.Location, "test.clsidmap"); string json = JsonConvert.SerializeObject(clsidMap); string comHostName = "comhost.dll"; File.WriteAllText(clsidmapPath, json); - string regFreeComManifestPath = Path.Combine(directory.Path, "test.manifest"); + string regFreeComManifestPath = Path.Combine(directory.Location, "test.manifest"); RegFreeComManifest.CreateManifestFromClsidmap(assemblyName, comHostName, assemblyVersion, clsidmapPath, regFreeComManifestPath); @@ -84,7 +83,7 @@ public void EntryInClsidMapAddedToRegFreeComManifest() [Fact] public void EntryInClsidMapAddedToRegFreeComManifestIncludesProgId() { - using TestDirectory directory = TestDirectory.Create(); + using TestArtifact directory = TestArtifact.Create(nameof(EntryInClsidMapAddedToRegFreeComManifestIncludesProgId)); string guid = "{190f1974-fa98-4922-8ed4-cf748630abbe}"; string assemblyName = "ComLibrary"; string typeName = "ComLibrary.Server"; @@ -98,13 +97,13 @@ public void EntryInClsidMapAddedToRegFreeComManifestIncludesProgId() } }; - string clsidmapPath = Path.Combine(directory.Path, "test.clsidmap"); + string clsidmapPath = Path.Combine(directory.Location, "test.clsidmap"); string json = JsonConvert.SerializeObject(clsidMap); string comHostName = "comhost.dll"; File.WriteAllText(clsidmapPath, json); - string regFreeComManifestPath = Path.Combine(directory.Path, "test.manifest"); + string regFreeComManifestPath = Path.Combine(directory.Location, "test.manifest"); RegFreeComManifest.CreateManifestFromClsidmap(assemblyName, comHostName, assemblyVersion, clsidmapPath, regFreeComManifestPath); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppWithUnknownLanguageResource.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppWithUnknownLanguageResource.cs deleted file mode 100644 index 8e57c2ada74e2f..00000000000000 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppWithUnknownLanguageResource.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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 Microsoft.DotNet.CoreSetup.Test; -using Xunit; - -namespace Microsoft.NET.HostModel.Tests -{ - // https://github.com/dotnet/runtime/issues/88465 - public class AppWithUnknownLanguageResource : IClassFixture - { - private SharedTestState sharedTestState; - - public AppWithUnknownLanguageResource(SharedTestState fixture) - { - sharedTestState = fixture; - } - - [Fact] - private void Can_Build_App_With_Resource_With_Unknown_Language() - { - var fixture = sharedTestState.TestFixture.Copy(); - - fixture.TestProject.BuiltApp.CreateAppHost(); - } - - public class SharedTestState : IDisposable - { - public RepoDirectoriesProvider RepoDirectories { get; set; } - public TestProjectFixture TestFixture { get; set; } - - public SharedTestState() - { - RepoDirectories = new RepoDirectoriesProvider(); - var testFixture = new TestProjectFixture("AppWithUnknownLanguageResource", RepoDirectories); - testFixture.EnsureRestored().BuildProject(); - TestFixture = testFixture; - } - - public void Dispose() - { - TestFixture.Dispose(); - } - } - } -} diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/Microsoft.NET.HostModel.AppHost.Tests.csproj b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/Microsoft.NET.HostModel.AppHost.Tests.csproj deleted file mode 100644 index 012d7ce6261d21..00000000000000 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/Microsoft.NET.HostModel.AppHost.Tests.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Microsoft.NET.HostModel.AppHost Tests - $(TestInfraTargetFramework) - Microsoft.NET.HostModel.AppHost.Tests - Microsoft.NET.HostModel.AppHost.Tests - true - - hma - true - - - - - - - - diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/Microsoft.NET.HostModel.Bundle.Tests.csproj b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/Microsoft.NET.HostModel.Bundle.Tests.csproj deleted file mode 100644 index ad94f762b30442..00000000000000 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/Microsoft.NET.HostModel.Bundle.Tests.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Microsoft.NET.HostModel.Bundle Tests - $(TestInfraTargetFramework) - Microsoft.NET.HostModel.Bundle.Tests - Microsoft.NET.HostModel.Bundle.Tests - true - - hmb - true - - - - - - - - diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/Microsoft.NET.HostModel.ComHost.Tests.csproj b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/Microsoft.NET.HostModel.ComHost.Tests.csproj deleted file mode 100644 index 4f8d6cb298aff2..00000000000000 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/Microsoft.NET.HostModel.ComHost.Tests.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Microsoft.NET.HostModel.ComHost Tests - $(TestInfraTargetFramework) - Microsoft.NET.HostModel.ComHost.Tests - Microsoft.NET.HostModel.ComHost.Tests - true - - hmc - true - - - - - - - - diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/TestDirectory.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/TestDirectory.cs deleted file mode 100644 index e4dac9750cc200..00000000000000 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.ComHost.Tests/TestDirectory.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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.IO; -using System.Runtime.CompilerServices; - -namespace Microsoft.NET.HostModel.ComHost.Tests -{ - internal class TestDirectory : IDisposable - { - public string Path { get; private set; } - - private TestDirectory(string path) - { - Path = path; - Directory.CreateDirectory(path); - } - - public static TestDirectory Create([CallerMemberName] string callingMethod = "") - { - string path = System.IO.Path.Combine( - System.IO.Path.GetTempPath(), - "dotNetSdkUnitTest_" + callingMethod + (Guid.NewGuid().ToString().Substring(0, 8))); - return new TestDirectory(path); - } - - public void Dispose() - { - if (Directory.Exists(Path)) - { - Directory.Delete(Path, true); - } - } - } -} diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Tests.csproj b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Tests.csproj new file mode 100644 index 00000000000000..e809026a29c46c --- /dev/null +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Tests.csproj @@ -0,0 +1,18 @@ + + + + Microsoft.NET.HostModel.Tests + $(TestInfraTargetFramework) + Microsoft.NET.HostModel.Tests + true + + hm + true + + + + + + + + diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/ResourceUpdaterTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/ResourceUpdaterTests.cs similarity index 100% rename from src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/ResourceUpdaterTests.cs rename to src/installer/tests/Microsoft.NET.HostModel.Tests/ResourceUpdaterTests.cs From 3133abb1476ef5ca419d0163a60d6ccab4ef91ec Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Mon, 22 Jan 2024 13:53:12 -0800 Subject: [PATCH 183/189] Don't copy runner to test output during regular builds (#96826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't copy runner to test output during regular builds The test runner is not required for local test execution, but is needed for helix (today). In the future we should try to change helix to not expect the runner in the output directory, but instead as a correlation payload. * Refine xunit 1651 workaround until we can pick up the fix * Update eng/testing/xunit/xunit.console.targets Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com> * Update xunit.console.targets --------- Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com> Co-authored-by: Viktor Hofer --- eng/testing/xunit/xunit.console.targets | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/eng/testing/xunit/xunit.console.targets b/eng/testing/xunit/xunit.console.targets index 175d149c96a914..f7bdcc2940ec02 100644 --- a/eng/testing/xunit/xunit.console.targets +++ b/eng/testing/xunit/xunit.console.targets @@ -68,8 +68,8 @@ - - + + - - - - - - - - From 64f6c16456057205baf9b1665c92829338eccee0 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 22 Jan 2024 17:57:50 -0500 Subject: [PATCH 184/189] Add more Arm64 special-casing to TensorPrimitives (#97331) * Add more Arm64 special-casing to TensorPrimitives * Address PR feedback --- .../netcore/TensorPrimitives.netcore.cs | 138 ++++++++---------- 1 file changed, 63 insertions(+), 75 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs index 1550e3d6bf7d4c..6a99db20fa180e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs @@ -9365,6 +9365,11 @@ private static Vector128 FusedMultiplyAdd(Vector128 x, Vector128 y, if (typeof(T) == typeof(float)) return AdvSimd.FusedMultiplyAdd(addend.AsSingle(), x.AsSingle(), y.AsSingle()).As(); } + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.FusedMultiplyAdd(addend.AsDouble(), x.AsDouble(), y.AsDouble()).As(); + } + return (x * y) + addend; } @@ -9867,17 +9872,19 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) public static Vector128 Invoke(Vector128 x) { - if (typeof(T) == typeof(float)) + if (Sse.IsSupported) { - if (Sse.IsSupported) - { - return Sse.Reciprocal(x.AsSingle()).As(); - } + if (typeof(T) == typeof(float)) return Sse.Reciprocal(x.AsSingle()).As(); + } - if (AdvSimd.IsSupported) - { - return AdvSimd.ReciprocalEstimate(x.AsSingle()).As(); - } + if (AdvSimd.IsSupported) + { + if (typeof(T) == typeof(float)) return AdvSimd.ReciprocalEstimate(x.AsSingle()).As(); + } + + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.ReciprocalEstimate(x.AsDouble()).As(); } return Vector128.One / x; @@ -9885,12 +9892,9 @@ public static Vector128 Invoke(Vector128 x) public static Vector256 Invoke(Vector256 x) { - if (typeof(T) == typeof(float)) + if (Avx.IsSupported) { - if (Avx.IsSupported) - { - return Avx.Reciprocal(x.AsSingle()).As(); - } + if (typeof(T) == typeof(float)) return Avx.Reciprocal(x.AsSingle()).As(); } return Vector256.One / x; @@ -9900,15 +9904,8 @@ public static Vector512 Invoke(Vector512 x) { if (Avx512F.IsSupported) { - if (typeof(T) == typeof(float)) - { - return Avx512F.Reciprocal14(x.AsSingle()).As(); - } - - if (typeof(T) == typeof(double)) - { - return Avx512F.Reciprocal14(x.AsDouble()).As(); - } + if (typeof(T) == typeof(float)) return Avx512F.Reciprocal14(x.AsSingle()).As(); + if (typeof(T) == typeof(double)) return Avx512F.Reciprocal14(x.AsDouble()).As(); } return Vector512.One / x; @@ -9923,17 +9920,19 @@ public static Vector512 Invoke(Vector512 x) public static Vector128 Invoke(Vector128 x) { - if (typeof(T) == typeof(float)) + if (Sse.IsSupported) { - if (Sse.IsSupported) - { - return Sse.ReciprocalSqrt(x.AsSingle()).As(); - } + if (typeof(T) == typeof(float)) return Sse.ReciprocalSqrt(x.AsSingle()).As(); + } - if (AdvSimd.IsSupported) - { - return AdvSimd.ReciprocalSquareRootEstimate(x.AsSingle()).As(); - } + if (AdvSimd.IsSupported) + { + if (typeof(T) == typeof(float)) return AdvSimd.ReciprocalSquareRootEstimate(x.AsSingle()).As(); + } + + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.ReciprocalSquareRootEstimate(x.AsDouble()).As(); } return Vector128.One / Vector128.Sqrt(x); @@ -9941,12 +9940,9 @@ public static Vector128 Invoke(Vector128 x) public static Vector256 Invoke(Vector256 x) { - if (typeof(T) == typeof(float)) + if (Avx.IsSupported) { - if (Avx.IsSupported) - { - return Avx.ReciprocalSqrt(x.AsSingle()).As(); - } + if (typeof(T) == typeof(float)) return Avx.ReciprocalSqrt(x.AsSingle()).As(); } return Vector256.One / Vector256.Sqrt(x); @@ -9956,15 +9952,8 @@ public static Vector512 Invoke(Vector512 x) { if (Avx512F.IsSupported) { - if (typeof(T) == typeof(float)) - { - return Avx512F.ReciprocalSqrt14(x.AsSingle()).As(); - } - - if (typeof(T) == typeof(double)) - { - return Avx512F.ReciprocalSqrt14(x.AsDouble()).As(); - } + if (typeof(T) == typeof(float)) return Avx512F.ReciprocalSqrt14(x.AsSingle()).As(); + if (typeof(T) == typeof(double)) return Avx512F.ReciprocalSqrt14(x.AsDouble()).As(); } return Vector512.One / Vector512.Sqrt(x); @@ -10042,6 +10031,11 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) if (typeof(T) == typeof(float)) return AdvSimd.Max(x.AsSingle(), y.AsSingle()).As(); } + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.Max(x.AsDouble(), y.AsDouble()).As(); + } + if (typeof(T) == typeof(float)) { return @@ -10694,6 +10688,11 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) if (typeof(T) == typeof(float)) return AdvSimd.Max(x.AsSingle(), y.AsSingle()).As(); } + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.Max(x.AsDouble(), y.AsDouble()).As(); + } + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { return @@ -10938,6 +10937,11 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) if (typeof(T) == typeof(float)) return AdvSimd.Min(x.AsSingle(), y.AsSingle()).As(); } + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.Min(x.AsDouble(), y.AsDouble()).As(); + } + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { return @@ -11003,6 +11007,11 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) if (typeof(T) == typeof(float)) return AdvSimd.Min(x.AsSingle(), y.AsSingle()).As(); } + if (AdvSimd.Arm64.IsSupported) + { + if (typeof(T) == typeof(double)) return AdvSimd.Arm64.Min(x.AsDouble(), y.AsDouble()).As(); + } + if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { return @@ -14000,15 +14009,8 @@ public static Vector128 Invoke(Vector128 x) { if (typeof(T) == typeof(float)) { - if (Sse41.IsSupported) - { - return Sse41.RoundToZero(x.AsSingle()).As(); - } - - if (AdvSimd.IsSupported) - { - return AdvSimd.RoundToZero(x.AsSingle()).As(); - } + if (Sse41.IsSupported) return Sse41.RoundToZero(x.AsSingle()).As(); + if (AdvSimd.IsSupported) return AdvSimd.RoundToZero(x.AsSingle()).As(); return Vector128.ConditionalSelect(Vector128.GreaterThanOrEqual(x, Vector128.Zero), Vector128.Floor(x.AsSingle()).As(), @@ -14017,10 +14019,8 @@ public static Vector128 Invoke(Vector128 x) if (typeof(T) == typeof(double)) { - if (Sse41.IsSupported) - { - return Sse41.RoundToZero(x.AsDouble()).As(); - } + if (Sse41.IsSupported) return Sse41.RoundToZero(x.AsDouble()).As(); + if (AdvSimd.Arm64.IsSupported) return AdvSimd.Arm64.RoundToZero(x.AsDouble()).As(); return Vector128.ConditionalSelect(Vector128.GreaterThanOrEqual(x, Vector128.Zero), Vector128.Floor(x.AsDouble()).As(), @@ -14034,10 +14034,7 @@ public static Vector256 Invoke(Vector256 x) { if (typeof(T) == typeof(float)) { - if (Avx.IsSupported) - { - return Avx.RoundToZero(x.AsSingle()).As(); - } + if (Avx.IsSupported) return Avx.RoundToZero(x.AsSingle()).As(); return Vector256.ConditionalSelect(Vector256.GreaterThanOrEqual(x, Vector256.Zero), Vector256.Floor(x.AsSingle()).As(), @@ -14046,10 +14043,7 @@ public static Vector256 Invoke(Vector256 x) if (typeof(T) == typeof(double)) { - if (Avx.IsSupported) - { - return Avx.RoundToZero(x.AsDouble()).As(); - } + if (Avx.IsSupported) return Avx.RoundToZero(x.AsDouble()).As(); return Vector256.ConditionalSelect(Vector256.GreaterThanOrEqual(x, Vector256.Zero), Vector256.Floor(x.AsDouble()).As(), @@ -14063,10 +14057,7 @@ public static Vector512 Invoke(Vector512 x) { if (typeof(T) == typeof(float)) { - if (Avx512F.IsSupported) - { - return Avx512F.RoundScale(x.AsSingle(), 0b11).As(); - } + if (Avx512F.IsSupported) return Avx512F.RoundScale(x.AsSingle(), 0b11).As(); return Vector512.ConditionalSelect(Vector512.GreaterThanOrEqual(x, Vector512.Zero), Vector512.Floor(x.AsSingle()).As(), @@ -14075,10 +14066,7 @@ public static Vector512 Invoke(Vector512 x) if (typeof(T) == typeof(double)) { - if (Avx512F.IsSupported) - { - return Avx512F.RoundScale(x.AsDouble(), 0b11).As(); - } + if (Avx512F.IsSupported) return Avx512F.RoundScale(x.AsDouble(), 0b11).As(); return Vector512.ConditionalSelect(Vector512.GreaterThanOrEqual(x, Vector512.Zero), Vector512.Floor(x.AsDouble()).As(), From 4ecb85787c7be226c384543fc5dcccde0bc6abb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:58:54 -0800 Subject: [PATCH 185/189] Bump branding to .NET 9.0 Preview 1 (#97336) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 42de1573491ce8..8eca423cfc6cb1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -10,7 +10,7 @@ 8.0.0 7.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet8)').Build),14)) 6.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet7)').Build),11)) - alpha + preview 1 false From e68bae1a1a1396cc406587010302ad9da73b1c4f Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Mon, 22 Jan 2024 19:38:11 -0500 Subject: [PATCH 186/189] [mono][aot] Add a workaround for #97342. (#97347) --- src/mono/mono/mini/aot-compiler.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index 42c122d98371bc..e30602dbca0bd6 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -6688,6 +6688,9 @@ compute_line_numbers (MonoMethod *method, int code_size, MonoDebugMethodJitInfo mono_debug_free_source_location (loc); continue; } + if (loc->row > 0xffffff) + // FIXME: Invalid debug info + continue; prev_line = loc->row; //printf ("D: %s:%d il=%x native=%x\n", loc->source_file, loc->row, il_offset, i); if (first) From bb74bb79db53eae474977a0928756a0db7fc6a3f Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 22 Jan 2024 17:07:31 -0800 Subject: [PATCH 187/189] JIT: ARM64 SVE format encodings, `SVE_JD_4A` to `SVE_JN_3B` (#97129) * Added SVE_JD_4A format * Added SVE_JD_4B format * Added SVE_JJ_4A format, added more formats but they are commented out for now * Added SVE_JJ_4A_B format * Revert minor change * Minor cleanup * Cleanup. Fixed a few formats. Introduced new INS_OPTS_SCALABLE options for UXTW and SXTW * Added SVE_JJ_4A_C format * Added SVE_JJ_4A_D format * Added SVE_JK_4A and SVE_JK_4A_B formats * Added SVE_JN_3A format * Added SVE_JN_3B format * Some comments * Feedback * Feedback * fix build * Formatting --- src/coreclr/jit/codegenarm64test.cpp | 104 +++++ src/coreclr/jit/emitarm64.cpp | 568 ++++++++++++++++++++++++++- src/coreclr/jit/emitarm64.h | 15 + src/coreclr/jit/instr.h | 8 + 4 files changed, 688 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 03edaae7366903..f7c9567a358724 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5881,6 +5881,110 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_I(INS_sve_st4w, EA_SCALABLE, REG_V31, REG_P1, REG_R5, 28, INS_OPTS_SCALABLE_S); // ST4W {.S, .S, .S, .S }, , [{, // #, MUL VL}] + + // IF_SVE_JD_4A + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V4, REG_P1, REG_R2, REG_R0, + INS_OPTS_SCALABLE_B); // ST1B {.}, , [, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V4, REG_P5, REG_R6, REG_R2, + INS_OPTS_SCALABLE_H); // ST1B {.}, , [, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V6, REG_P5, REG_R7, REG_R4, + INS_OPTS_SCALABLE_S); // ST1B {.}, , [, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V4, REG_P0, REG_R1, REG_R2, + INS_OPTS_SCALABLE_D); // ST1B {.}, , [, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V5, REG_P6, REG_R1, REG_R2, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_LSL_N); // ST1H {.}, , [, , LSL #1] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V1, REG_P2, REG_R3, REG_R4, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_LSL_N); // ST1H {.}, , [, , LSL #1] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V3, REG_P2, REG_R4, REG_R0, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_LSL_N); // ST1H {.}, , [, , LSL #1] + + // IF_SVE_JD_4B + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V0, REG_P1, REG_R2, REG_R3, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_LSL_N); // ST1W {.}, , [, , LSL #2] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V2, REG_P3, REG_R4, REG_R5, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_LSL_N); // ST1W {.}, , [, , LSL #2] + + // IF_SVE_JJ_4A + theEmitter->emitIns_R_R_R_R(INS_sve_st1d, EA_SCALABLE, REG_V0, REG_P1, REG_R2, REG_V3, INS_OPTS_SCALABLE_D_UXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1D {.D }, , [, .D, #3] + theEmitter->emitIns_R_R_R_R(INS_sve_st1d, EA_SCALABLE, REG_V0, REG_P1, REG_R2, REG_V3, INS_OPTS_SCALABLE_D_SXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1D {.D }, , [, .D, #3] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V3, REG_P1, REG_R5, REG_V4, INS_OPTS_SCALABLE_S_UXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1H {.S }, , [, .S, #1] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V3, REG_P1, REG_R5, REG_V4, INS_OPTS_SCALABLE_S_SXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1H {.S }, , [, .S, #1] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V0, REG_P3, REG_R1, REG_V2, INS_OPTS_SCALABLE_S_UXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1W {.S }, , [, .S, #2] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V0, REG_P3, REG_R1, REG_V2, INS_OPTS_SCALABLE_S_SXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1W {.S }, , [, .S, #2] + + // IF_SVE_JJ_4A_B + theEmitter->emitIns_R_R_R_R(INS_sve_st1d, EA_SCALABLE, REG_V3, REG_P1, REG_R2, REG_V5, + INS_OPTS_SCALABLE_D_UXTW); // ST1D {.D }, , [, .D, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1d, EA_SCALABLE, REG_V3, REG_P1, REG_R2, REG_V5, + INS_OPTS_SCALABLE_D_SXTW); // ST1D {.D }, , [, .D, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V2, REG_P3, REG_R1, REG_V4, INS_OPTS_SCALABLE_D_UXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1H {.D }, , [, .D, #1] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V2, REG_P3, REG_R1, REG_V4, INS_OPTS_SCALABLE_D_SXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1H {.D }, , [, .D, #1] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V1, REG_P4, REG_R2, REG_V3, INS_OPTS_SCALABLE_D_UXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1W {.D }, , [, .D, #2] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V1, REG_P4, REG_R2, REG_V3, INS_OPTS_SCALABLE_D_SXTW, + INS_SCALABLE_OPTS_MOD_N); // ST1W {.D }, , [, .D, #2] + + // IF_SVE_JJ_4A_C + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V1, REG_P5, REG_R1, REG_V3, + INS_OPTS_SCALABLE_D_UXTW); // ST1H {.D }, , [, .D, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V1, REG_P5, REG_R1, REG_V3, + INS_OPTS_SCALABLE_D_SXTW); // ST1H {.D }, , [, .D, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V0, REG_P2, REG_R3, REG_V4, + INS_OPTS_SCALABLE_D_UXTW); // ST1W {.D }, , [, .D, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V0, REG_P2, REG_R3, REG_V4, + INS_OPTS_SCALABLE_D_SXTW); // ST1W {.D }, , [, .D, ] + + // IF_SVE_JJ_4A_D + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V7, REG_P5, REG_R4, REG_V1, + INS_OPTS_SCALABLE_S_UXTW); // ST1H {.S }, , [, .S, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1h, EA_SCALABLE, REG_V7, REG_P5, REG_R4, REG_V1, + INS_OPTS_SCALABLE_S_SXTW); // ST1H {.S }, , [, .S, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V1, REG_P2, REG_R3, REG_V2, + INS_OPTS_SCALABLE_S_UXTW); // ST1W {.S }, , [, .S, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1w, EA_SCALABLE, REG_V1, REG_P2, REG_R3, REG_V2, + INS_OPTS_SCALABLE_S_SXTW); // ST1W {.S }, , [, .S, ] + + // IF_SVE_JK_4A + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V4, REG_P2, REG_R0, REG_V1, + INS_OPTS_SCALABLE_D_UXTW); // ST1B {.D }, , [, .D, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V4, REG_P2, REG_R0, REG_V1, + INS_OPTS_SCALABLE_D_SXTW); // ST1B {.D }, , [, .D, ] + + // IF_SVE_JK_4A_B + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V1, REG_P4, REG_R3, REG_V0, + INS_OPTS_SCALABLE_S_UXTW); // ST1B {.S }, , [, .S, ] + theEmitter->emitIns_R_R_R_R(INS_sve_st1b, EA_SCALABLE, REG_V1, REG_P4, REG_R3, REG_V0, + INS_OPTS_SCALABLE_S_SXTW); // ST1B {.S }, , [, .S, ] + + // IF_SVE_JN_3A + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V3, REG_P2, REG_R1, 5, + INS_OPTS_SCALABLE_B); // ST1B {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V3, REG_P2, REG_R1, 4, + INS_OPTS_SCALABLE_H); // ST1B {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V0, REG_P3, REG_R4, 3, + INS_OPTS_SCALABLE_H); // ST1H {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V3, REG_P2, REG_R1, 2, + INS_OPTS_SCALABLE_S); // ST1B {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V0, REG_P3, REG_R4, 1, + INS_OPTS_SCALABLE_S); // ST1H {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V3, REG_P2, REG_R1, 0, + INS_OPTS_SCALABLE_D); // ST1B {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V0, REG_P3, REG_R4, -2, + INS_OPTS_SCALABLE_D); // ST1H {.}, , [{, #, MUL VL}] + + // IF_SVE_JN_3B + theEmitter->emitIns_R_R_R_I(INS_sve_st1w, EA_SCALABLE, REG_V2, REG_P1, REG_R3, 5, + INS_OPTS_SCALABLE_S); // ST1W {.}, , [{, #, MUL VL}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1w, EA_SCALABLE, REG_V2, REG_P1, REG_R3, 1, + INS_OPTS_SCALABLE_D); // ST1W {.}, , [{, #, MUL VL}] } #endif // defined(TARGET_ARM64) && defined(DEBUG) diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 5c9fb990bb8591..d46f6c3cc3a801 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1500,6 +1500,70 @@ void emitter::emitInsSanityCheck(instrDesc* id) } break; + case IF_SVE_JD_4A: // .........xxmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + elemsize = id->idOpSize(); + assert(isVectorRegister(id->idReg1())); // ttttt + assert(isPredicateRegister(id->idReg2())); // ggg + assert(isGeneralRegister(id->idReg3())); // nnnnn + assert(isGeneralRegister(id->idReg4())); // mmmmm + assert(isScalableVectorSize(elemsize)); // xx + // st1h is reserved for scalable B + assert((id->idIns() == INS_sve_st1h) ? insOptsScalableAtLeastHalf(id->idInsOpt()) + : insOptsScalableStandard(id->idInsOpt())); + break; + + case IF_SVE_JD_4B: // ..........xmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ttttt + assert(isPredicateRegister(id->idReg2())); // ggg + assert(isGeneralRegister(id->idReg3())); // nnnnn + assert(isGeneralRegister(id->idReg4())); // mmmmm + assert(isScalableVectorSize(elemsize)); // x + break; + + case IF_SVE_JJ_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_C: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_D: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JK_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit unscaled + // offsets) + case IF_SVE_JK_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit + // unscaled offsets) + elemsize = id->idOpSize(); + assert(insOptsScalable32bitExtends(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ttttt + assert(isPredicateRegister(id->idReg2())); // ggg + assert(isGeneralRegister(id->idReg3())); // nnnnn + assert(isScalableVectorSize(elemsize)); + break; + + case IF_SVE_JN_3A: // .........xx.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + imm = emitGetInsSC(id); + elemsize = id->idOpSize(); + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ttttt + assert(isPredicateRegister(id->idReg2())); // ggg + assert(isGeneralRegister(id->idReg3())); // nnnnn + assert(isScalableVectorSize(elemsize)); // xx + assert(isValidSimm4(imm)); // iiii + break; + + case IF_SVE_JN_3B: // ..........x.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + imm = emitGetInsSC(id); + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ttttt + assert(isPredicateRegister(id->idReg2())); // ggg + assert(isGeneralRegister(id->idReg3())); // nnnnn + assert(isScalableVectorSize(elemsize)); // x + assert(isValidSimm4(imm)); // iiii + break; + default: printf("unexpected format %s\n", emitIfName(id->idInsFmt())); assert(!"Unexpected format"); @@ -5595,9 +5659,13 @@ emitter::code_t emitter::emitInsCodeSve(instruction ins, insFormat fmt) return EA_2BYTE; case INS_OPTS_SCALABLE_S: + case INS_OPTS_SCALABLE_S_UXTW: + case INS_OPTS_SCALABLE_S_SXTW: return EA_4BYTE; case INS_OPTS_SCALABLE_D: + case INS_OPTS_SCALABLE_D_UXTW: + case INS_OPTS_SCALABLE_D_SXTW: return EA_8BYTE; case INS_OPTS_SCALABLE_Q: @@ -10441,17 +10509,24 @@ void emitter::emitIns_R_R_R_I(instruction ins, } else { -#if DEBUG - if (ins == INS_sve_st1w) + if ((ins == INS_sve_st1w) && insOptsScalableWords(opt)) { - assert(opt == INS_OPTS_SCALABLE_Q); + fmt = IF_SVE_JN_3B; } else { - assert(opt == INS_OPTS_SCALABLE_D); - } +#if DEBUG + if (ins == INS_sve_st1w) + { + assert(opt == INS_OPTS_SCALABLE_Q); + } + else + { + assert(opt == INS_OPTS_SCALABLE_D); + } #endif // DEBUG - fmt = IF_SVE_JN_3C; + fmt = IF_SVE_JN_3C; + } } break; @@ -10536,6 +10611,17 @@ void emitter::emitIns_R_R_R_I(instruction ins, fmt = IF_SVE_JO_3A; break; + case INS_sve_st1b: + case INS_sve_st1h: + assert(isVectorRegister(reg1)); + assert(isPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidSimm4(imm)); + // st1h is reserved for scalable B + assert((ins == INS_sve_st1h) ? insOptsScalableAtLeastHalf(opt) : insOptsScalableStandard(opt)); + fmt = IF_SVE_JN_3A; + break; + default: unreached(); break; @@ -11065,6 +11151,166 @@ void emitter::emitIns_R_R_R_R(instruction ins, fmt = IF_SVE_AS_4A; break; + case INS_sve_st1b: + assert(isVectorRegister(reg1)); + assert(isPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isScalableVectorSize(size)); + assert(insScalableOptsNone(sopt)); + if (insOptsScalableStandard(opt)) + { + assert(isGeneralRegister(reg4)); + fmt = IF_SVE_JD_4A; + } + else + { + assert(insOptsScalable32bitExtends(opt)); + switch (opt) + { + case INS_OPTS_SCALABLE_S_UXTW: + case INS_OPTS_SCALABLE_S_SXTW: + fmt = IF_SVE_JK_4A_B; + break; + + case INS_OPTS_SCALABLE_D_UXTW: + case INS_OPTS_SCALABLE_D_SXTW: + fmt = IF_SVE_JK_4A; + break; + + default: + assert(!"Invalid options for scalable"); + break; + } + } + break; + + case INS_sve_st1h: + assert(isVectorRegister(reg1)); + assert(isPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isScalableVectorSize(size)); + if (insOptsScalableStandard(opt)) + { + // st1h is reserved for scalable B + assert((ins == INS_sve_st1h) ? insOptsScalableAtLeastHalf(opt) : true); + assert(isGeneralRegister(reg4)); + assert(sopt == INS_SCALABLE_OPTS_LSL_N); + fmt = IF_SVE_JD_4A; + } + else + { + assert(insOptsScalable32bitExtends(opt)); + switch (opt) + { + case INS_OPTS_SCALABLE_S_UXTW: + case INS_OPTS_SCALABLE_S_SXTW: + if (insScalableOptsNone(sopt)) + { + fmt = IF_SVE_JJ_4A_D; + } + else + { + assert(sopt == INS_SCALABLE_OPTS_MOD_N); + fmt = IF_SVE_JJ_4A; + } + break; + + case INS_OPTS_SCALABLE_D_UXTW: + case INS_OPTS_SCALABLE_D_SXTW: + if (insScalableOptsNone(sopt)) + { + fmt = IF_SVE_JJ_4A_C; + } + else + { + assert(sopt == INS_SCALABLE_OPTS_MOD_N); + fmt = IF_SVE_JJ_4A_B; + } + break; + + default: + assert(!"Invalid options for scalable"); + break; + } + } + break; + + case INS_sve_st1w: + assert(isVectorRegister(reg1)); + assert(isPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isScalableVectorSize(size)); + if (insOptsScalableStandard(opt)) + { + assert(isGeneralRegister(reg4)); + assert(sopt == INS_SCALABLE_OPTS_LSL_N); + fmt = IF_SVE_JD_4B; + } + else + { + assert(insOptsScalable32bitExtends(opt)); + switch (opt) + { + case INS_OPTS_SCALABLE_S_UXTW: + case INS_OPTS_SCALABLE_S_SXTW: + if (insScalableOptsNone(sopt)) + { + fmt = IF_SVE_JJ_4A_D; + } + else + { + assert(sopt == INS_SCALABLE_OPTS_MOD_N); + fmt = IF_SVE_JJ_4A; + } + break; + + case INS_OPTS_SCALABLE_D_UXTW: + case INS_OPTS_SCALABLE_D_SXTW: + if (insScalableOptsNone(sopt)) + { + fmt = IF_SVE_JJ_4A_C; + } + else + { + assert(sopt == INS_SCALABLE_OPTS_MOD_N); + fmt = IF_SVE_JJ_4A_B; + } + break; + + default: + assert(!"Invalid options for scalable"); + break; + } + } + break; + + case INS_sve_st1d: + assert(insOptsScalable32bitExtends(opt)); + assert(isVectorRegister(reg1)); + assert(isPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isScalableVectorSize(size)); + switch (opt) + { + case INS_OPTS_SCALABLE_D_UXTW: + case INS_OPTS_SCALABLE_D_SXTW: + if (sopt == INS_SCALABLE_OPTS_MOD_N) + { + fmt = IF_SVE_JJ_4A; + } + else + { + assert(insScalableOptsNone(sopt)); + fmt = IF_SVE_JJ_4A_B; + } + break; + + default: + assert(!"Invalid options for scalable"); + break; + } + break; + default: unreached(); break; @@ -13893,6 +14139,56 @@ void emitter::emitIns_Call(EmitCallType callType, return 0; } +/***************************************************************************** + * + * Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 Sve vector instruction + * This specifically encodes the size at bit locations '22-21'. + */ + +/*static*/ emitter::code_t emitter::insEncodeSveElemsize_22_to_21(emitAttr size) +{ + switch (size) + { + case EA_1BYTE: + return 0; + + case EA_2BYTE: + return (1 << 21); // set the bit at location 21 + + case EA_4BYTE: + return (1 << 22); // set the bit at location 22 + + case EA_8BYTE: + return (1 << 22) | (1 << 21); // set the bit at location 22 and 21 + + default: + assert(!"Invalid insOpt for vector register"); + } + return 0; +} + +/***************************************************************************** + * + * Returns the encoding to select the 4/8 byte elemsize for an Arm64 Sve vector instruction + * This specifically encodes the field 'sz' at bit location '21'. + */ + +/*static*/ emitter::code_t emitter::insEncodeSveElemsize_sz_21(emitAttr size) +{ + switch (size) + { + case EA_4BYTE: + return 0; + + case EA_8BYTE: + return (1 << 21); + + default: + assert(!"Invalid insOpt for vector register"); + } + return 0; +} + /***************************************************************************** * * Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 Sve vector instruction @@ -14003,6 +14299,7 @@ void emitter::emitIns_Call(EmitCallType callType, case INS_sve_ldff1d: case INS_sve_ldff1sw: case INS_sve_st1b: + case INS_sve_st1h: case INS_sve_ldff1sb: case INS_sve_ldff1b: case INS_sve_ldnt1sb: @@ -16926,6 +17223,80 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_JD_4A: // .........xxmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeReg_R_20_to_16(id->idReg4()); // mmmmm + code |= insEncodeSveElemsize_22_to_21(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_JD_4B: // ..........xmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeReg_R_20_to_16(id->idReg4()); // mmmmm + code |= insEncodeSveElemsize_sz_21(optGetSveElemsize(id->idInsOpt())); // x + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_JJ_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // // offsets) + case IF_SVE_JJ_4A_C: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_D: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JK_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit unscaled + // offsets) + case IF_SVE_JK_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit + // unscaled offsets) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeReg_V_20_to_16(id->idReg4()); // mmmmm + + switch (id->idInsOpt()) + { + case INS_OPTS_SCALABLE_S_SXTW: + case INS_OPTS_SCALABLE_D_SXTW: + code |= (1 << 14); // h + break; + + default: + break; + } + + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_JN_3A: // .........xx.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeSimm4_19_to_16(imm); // iiii + code |= insEncodeSveElemsize_22_to_21(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_JN_3B: // ..........x.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeSimm4_19_to_16(imm); // iiii + code |= insEncodeSveElemsize_sz_21(optGetSveElemsize(id->idInsOpt())); // x + dst += emitOutput_Instr(dst, code); + break; + default: assert(!"Unexpected format"); break; @@ -17349,6 +17720,31 @@ void emitter::emitDispExtendOpts(insOpts opt) assert(!"Bad value"); } +/***************************************************************************** + * + * Prints the encoding for the Extend Type encoding + */ + +void emitter::emitDispSveExtendOpts(insOpts opt) +{ + switch (opt) + { + case INS_OPTS_SCALABLE_S_UXTW: + case INS_OPTS_SCALABLE_D_UXTW: + printf("uxtw"); + break; + + case INS_OPTS_SCALABLE_S_SXTW: + case INS_OPTS_SCALABLE_D_SXTW: + printf("sxtw"); + break; + + default: + assert(!"Bad value"); + break; + } +} + /***************************************************************************** * * Prints the encoding for the Extend Type encoding in loads/stores @@ -17387,7 +17783,7 @@ void emitter::emitDispReg(regNumber reg, emitAttr attr, bool addComma) // void emitter::emitDispSveReg(regNumber reg, insOpts opt, bool addComma) { - assert(insOptsScalable(opt)); + assert(insOptsScalable(opt) || insOptsScalable32bitExtends(opt)); assert(isVectorRegister(reg)); printf(emitSveRegName(reg)); emitDispArrangement(opt); @@ -17617,6 +18013,8 @@ void emitter::emitDispArrangement(insOpts opt) str = "4s"; break; case INS_OPTS_SCALABLE_S: + case INS_OPTS_SCALABLE_S_UXTW: + case INS_OPTS_SCALABLE_S_SXTW: str = "s"; break; case INS_OPTS_1D: @@ -17626,6 +18024,8 @@ void emitter::emitDispArrangement(insOpts opt) str = "2d"; break; case INS_OPTS_SCALABLE_D: + case INS_OPTS_SCALABLE_D_UXTW: + case INS_OPTS_SCALABLE_D_SXTW: str = "d"; break; case INS_OPTS_SCALABLE_Q: @@ -19627,6 +20027,140 @@ void emitter::emitDispInsHelp( printf("]"); break; + // {.}, , [, ] + // {.}, , [, , LSL #1] + case IF_SVE_JD_4A: // .........xxmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + emitDispSveConsecutiveRegList(id->idReg1(), insGetSveReg1ListSize(ins), id->idInsOpt(), true); // ttttt + emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg + printf("["); + emitDispReg(id->idReg3(), EA_8BYTE, true); // nnnnn + if (ins == INS_sve_st1h) + { + emitDispReg(id->idReg4(), EA_8BYTE, true); // mmmmm + printf("lsl #1]"); + } + else + { + emitDispReg(id->idReg4(), EA_8BYTE, false); // mmmmm + printf("]"); + } + break; + + // {.}, , [, , LSL #2] + case IF_SVE_JD_4B: // ..........xmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + emitDispSveConsecutiveRegList(id->idReg1(), insGetSveReg1ListSize(ins), id->idInsOpt(), true); // ttttt + emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg + printf("["); + emitDispReg(id->idReg3(), EA_8BYTE, true); // nnnnn + emitDispReg(id->idReg4(), EA_8BYTE, true); // mmmmm + printf("lsl #2]"); + break; + + // {.D }, , [, .D, #3] + // {.S }, , [, .S, #1] + // {.S }, , [, .S, #2] + case IF_SVE_JJ_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + // {.D }, , [, .D, ] + // {.D }, , [, .D, #1] + // {.D }, , [, .D, #2] + case IF_SVE_JJ_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + // {.D }, , [, .D, ] + case IF_SVE_JJ_4A_C: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + // {.S }, , [, .S, ] + case IF_SVE_JJ_4A_D: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + // {.D }, , [, .D, ] + case IF_SVE_JK_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit unscaled + // offsets) + // {.S }, , [, .S, ] + case IF_SVE_JK_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit + // unscaled offsets) + { + emitDispSveConsecutiveRegList(id->idReg1(), insGetSveReg1ListSize(ins), id->idInsOpt(), true); // ttttt + emitDispPredicateReg(id->idReg2(), PREDICATE_NONE, id->idInsOpt(), true); // ggg + printf("["); + emitDispReg(id->idReg3(), EA_8BYTE, true); // nnnnn + emitDispSveReg(id->idReg4(), id->idInsOpt(), true); // mmmmm + emitDispSveExtendOpts(id->idInsOpt()); + switch (ins) + { + case INS_sve_st1b: + printf("]"); + break; + + case INS_sve_st1h: + if ((fmt == IF_SVE_JJ_4A_C) || (fmt == IF_SVE_JJ_4A_D)) + { + printf("]"); + } + else + { + printf(" #1]"); + } + break; + + case INS_sve_st1w: + if ((fmt == IF_SVE_JJ_4A_C) || (fmt == IF_SVE_JJ_4A_D)) + { + printf("]"); + } + else + { + printf(" #2]"); + } + break; + + case INS_sve_st1d: + if (fmt == IF_SVE_JJ_4A_B) + { + printf("]"); + } + else + { + printf(" #3]"); + } + break; + + default: + assert(!"Invalid instruction"); + break; + } + break; + } + + // {.}, , [{, #, MUL VL}] + case IF_SVE_JN_3A: // .........xx.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + imm = emitGetInsSC(id); + emitDispSveConsecutiveRegList(id->idReg1(), insGetSveReg1ListSize(ins), id->idInsOpt(), true); // ttttt + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + printf("["); + emitDispReg(id->idReg3(), EA_8BYTE, imm != 0); // nnnnn + if (imm != 0) + { + emitDispImm(emitGetInsSC(id), true); // iiii + printf("mul vl"); + } + printf("]"); + break; + + // {.}, , [{, #, MUL VL}] + case IF_SVE_JN_3B: // ..........x.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + imm = emitGetInsSC(id); + emitDispSveConsecutiveRegList(id->idReg1(), insGetSveReg1ListSize(ins), id->idInsOpt(), true); // ttttt + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + printf("["); + emitDispReg(id->idReg3(), EA_8BYTE, imm != 0); // nnnnn + if (imm != 0) + { + emitDispImm(emitGetInsSC(id), true); // iiii + printf("mul vl"); + } + printf("]"); + break; + default: printf("unexpected format %s", emitIfName(id->idInsFmt())); assert(!"unexpectedFormat"); @@ -22487,6 +23021,26 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins } break; + case IF_SVE_JD_4A: // .........xxmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + case IF_SVE_JD_4B: // ..........xmmmmm ...gggnnnnnttttt -- SVE contiguous store (scalar plus scalar) + case IF_SVE_JJ_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_C: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JJ_4A_D: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit scaled + // offsets) + case IF_SVE_JK_4A: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit unscaled + // offsets) + case IF_SVE_JK_4A_B: // ...........mmmmm .h.gggnnnnnttttt -- SVE 64-bit scatter store (scalar plus 64-bit + // unscaled offsets) + case IF_SVE_JN_3A: // .........xx.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + case IF_SVE_JN_3B: // ..........x.iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + default: // all other instructions perfScoreUnhandledInstruction(id, &result); diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 04f8322a06fc94..8616c1c3109a59 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -47,6 +47,7 @@ void emitDispFlags(insCflags flags); void emitDispBarrier(insBarrier barrier); void emitDispShiftOpts(insOpts opt); void emitDispExtendOpts(insOpts opt); +void emitDispSveExtendOpts(insOpts opt); void emitDispLSExtendOpts(insOpts opt); void emitDispReg(regNumber reg, emitAttr attr, bool addComma); void emitDispSveReg(regNumber reg, insOpts opt, bool addComma); @@ -484,6 +485,14 @@ static code_t insEncodeReg3Scale(bool isScaled); // Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 SVE vector instruction static code_t insEncodeSveElemsize(emitAttr size); +// Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 Sve vector instruction +// This specifically encodes the size at bit locations '22-21'. +static code_t insEncodeSveElemsize_22_to_21(emitAttr size); + +// Returns the encoding to select the 4/8 byte elemsize for an Arm64 Sve vector instruction +// This specifically encodes the field 'sz' at bit location '21'. +static code_t insEncodeSveElemsize_sz_21(emitAttr size); + // Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 SVE vector instruction // This specifically encodes the field 'tszh:tszl' at bit locations '22:20-19'. static code_t insEncodeSveElemsize_tszh_22_tszl_20_to_19(emitAttr size); @@ -1043,6 +1052,12 @@ inline static bool insOptsScalableWide(insOpts opt) return ((opt == INS_OPTS_SCALABLE_B) || (opt == INS_OPTS_SCALABLE_H) || (opt == INS_OPTS_SCALABLE_S)); } +inline static bool insOptsScalable32bitExtends(insOpts opt) +{ + return ((opt == INS_OPTS_SCALABLE_S_UXTW) || (opt == INS_OPTS_SCALABLE_S_SXTW) || + (opt == INS_OPTS_SCALABLE_D_UXTW) || (opt == INS_OPTS_SCALABLE_D_SXTW)); +} + inline static bool insScalableOptsNone(insScalableOpts sopt) { // `sopt` is used for instructions with no extra encoding variants. diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index e9d8b461bf96d8..35b916d6f005b2 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -282,6 +282,11 @@ enum insOpts : unsigned INS_OPTS_SCALABLE_D, INS_OPTS_SCALABLE_Q, + INS_OPTS_SCALABLE_S_UXTW, + INS_OPTS_SCALABLE_S_SXTW, + INS_OPTS_SCALABLE_D_UXTW, + INS_OPTS_SCALABLE_D_SXTW, + INS_OPTS_MSL, // Vector Immediate (shifting ones variant) INS_OPTS_S_TO_4BYTE, // Single to INT32 @@ -323,6 +328,9 @@ enum insScalableOpts : unsigned INS_SCALABLE_OPTS_VL_2X, // Variants with a vector length specifier of 2x (eg whilege) INS_SCALABLE_OPTS_VL_4X, // Variants with a vector length specifier of 4x (eg whilege) + INS_SCALABLE_OPTS_LSL_N, // Variants with a LSL #N (eg {.}, , [, , LSL #2]) + INS_SCALABLE_OPTS_MOD_N, // Variants with a #N (eg {.S }, , [, .S, #2]) + // Removable once REG_V0 and REG_P0 are distinct INS_SCALABLE_OPTS_UNPREDICATED, // Variants without a predicate (eg add) INS_SCALABLE_OPTS_UNPREDICATED_WIDE, // Variants without a predicate and wide elements (eg asr) From cd0a72c86047559020d56c6a326af83d1cf25a13 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Jan 2024 18:59:20 -0800 Subject: [PATCH 188/189] Call VariantInit to initialize variants (#97349) --- src/tests/Interop/PInvoke/IEnumerator/IEnumeratorNative.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/Interop/PInvoke/IEnumerator/IEnumeratorNative.cpp b/src/tests/Interop/PInvoke/IEnumerator/IEnumeratorNative.cpp index a0d8703233f4b2..a42e6d2144d5f9 100644 --- a/src/tests/Interop/PInvoke/IEnumerator/IEnumeratorNative.cpp +++ b/src/tests/Interop/PInvoke/IEnumerator/IEnumeratorNative.cpp @@ -26,6 +26,7 @@ extern "C" DLL_EXPORT HRESULT STDMETHODCALLTYPE VerifyIntegerEnumerator(IEnumVAR HRESULT hr = S_OK; VARIANT element; + VariantInit(&element); ULONG numFetched; for (int i = start; i < start + count; ++i) @@ -68,6 +69,7 @@ extern "C" DLL_EXPORT HRESULT STDMETHODCALLTYPE VerifyIntegerEnumeration(IDispat { DISPPARAMS params{}; VARIANT result; + VariantInit(&result); HRESULT hr = pDisp->Invoke( DISPID_NEWENUM, IID_NULL, From e3547f1d3ac768a14a0598abf7868641ecf0c9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:24:49 -0800 Subject: [PATCH 189/189] Bump branding to .NET 9.0 Preview 2 (#97339) * Bump branding to .NET 9.0 Preview 1 * Bump branding to .NET 9.0 Preview 2 --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 8eca423cfc6cb1..0157bca90d7d19 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -11,7 +11,7 @@ 7.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet8)').Build),14)) 6.0.$([MSBuild]::Add($([System.Version]::Parse('$(PackageVersionNet7)').Build),11)) preview - 1 + 2 false release